Tengo un marco de datos con cada fila que tiene un valor de lista.
id list_of_value
0 ['a','b','c']
1 ['d','b','c']
2 ['a','b','c']
3 ['a','b','c']
Tengo que calcular una puntuación con una fila y contra todas las otras filas
Por ejemplo:
Step 1: Take value of id 0: ['a','b','c'],
Step 2: find the intersection between id 0 and id 1 ,
resultant = ['b','c']
Step 3: Score Calculation => resultant.size / id.size
repita el paso 2,3 entre id 0 e id 1,2,3, de manera similar para todos los identificadores.
y crear un marco de datos N x N; como esto:
- 0 1 2 3
0 1 0.6 1 1
1 1 1 1 1
2 1 1 1 1
3 1 1 1 1
En este momento mi código solo tiene uno para el bucle:
def scoreCalc(x,queryTData):
#mathematical calculation
commonTData = np.intersect1d(np.array(x),queryTData)
return commonTData.size/queryTData.size
ids = list(df['feed_id'])
dfSim = pd.DataFrame()
for indexQFID in range(len(ids)):
queryTData = np.array(df.loc[df['id'] == ids[indexQFID]]['list_of_value'].values.tolist())
dfSim[segmentDfFeedIds[indexQFID]] = segmentDf['list_of_value'].apply(scoreCalc,args=(queryTData,))
¿Hay una mejor manera de hacer esto? ¿puedo simplemente escribir una función de aplicación en lugar de hacer una iteración for-loop? ¿Puedo hacerlo más rápido?
list_of_value
?list_of_value
. Quiero decir en total, en todas las filas.Respuestas:
Si sus datos no son demasiado grandes, puede usar
get_dummies
para codificar los valores y hacer una multiplicación matricial:Salida:
Actualización : Aquí hay una breve explicación del código. La idea principal es convertir las listas dadas en codificadas en caliente:
Una vez que tenemos eso, el tamaño de la intersección de las dos filas, digamos,
0
y1
es solo su producto de punto, porque un personaje pertenece a ambas filas si y solo si está representado por1
ambas.Con eso en mente, primer uso
para convertir cada celda en una serie y concatenar todas esas series. Salida:
Ahora, usamos
pd.get_dummies
esa serie para convertirla en un marco de datos codificado en caliente:Como puede ver, cada valor tiene su propia fila. Como queremos combinar los que pertenecen a la misma fila original en una fila, podemos sumarlos por el índice original. Así
da el marco de datos codificado en binario que queremos. La proxima linea
es igual que su lógica:
s.dot(s.T)
calcula los productos de puntos por filas, luego.div(s.sum(1))
divide los recuentos por filas.fuente
12k x 12k
marco de datos. Debería estar bien si tiene alrededor de unos cientos de valores únicos.Prueba esto
Salida
También puedes hacerlo de la siguiente manera
fuente
Utilice la comprensión de la lista anidada en la lista de conjunto
s_list
. Dentro de la comprensión de la lista, utilice laintersection
operación para verificar la superposición y obtener la longitud de cada resultado. Finalmente, construya el marco de datos y divídalo por la longitud de cada lista endf.list_of_value
En caso de que haya valores duplicados en cada lista, debe usarlos en
collections.Counter
lugar deset
. Cambié los datos de muestra id = 0 a['a','a','c']
e id = 1 a['d','b','a']
fuente
Actualizado
Dado que se proponen muchas soluciones candidatas, parece una buena idea hacer un análisis de tiempo. Generé algunos datos aleatorios con 12k filas según lo solicitado por el OP, manteniendo los 3 elementos por conjunto pero ampliando el tamaño del alfabeto disponible para completar los conjuntos. Esto se puede ajustar para que coincida con los datos reales.
Avíseme si tiene una solución que le gustaría probar o actualizar.
Preparar
Ganador actual
Contendientes
Publicación original con detalles de la solución
Es posible hacer esto en
pandas
con una autounión.Como han señalado otras respuestas, el primer paso es desempaquetar los datos en una forma más larga.
Desde esta tabla es posible calcular los recuentos por ID.
Y luego viene la autounión, que ocurre en la
value
columna. Esto empareja las ID una vez para cada valor de intersección, por lo que las ID emparejadas se pueden contar para obtener los tamaños de intersección.Estos dos se pueden fusionar y calcular una puntuación.
Si prefiere la forma matricial, eso es posible con a
pivot
. Esta será una representación mucho más grande si los datos son escasos.fuente
Esta solución va a funcionar de manera eficiente con cualquier tamaño de los datos y cualquier tipo de valores en su
list
decir, sustr
oint
o de otra manera, también el cuidado de los valores repetitivos si los hubiere.En este caso, la comprensión de la lista funciona mejor porque no necesita cargar el atributo append de la lista y llamarlo como una función en cada iteración. En otras palabras y en general, las comprensiones de listas funcionan más rápido porque suspender y reanudar el marco de una función, o las funciones múltiples en otros casos son más lentas que crear una lista a pedido.
Usar una comprensión de la lista en lugar de un bucle que no construye una lista, acumular sin sentido una lista de valores sin sentido y luego desecharla, a menudo es más lento debido a la sobrecarga de crear y extender la lista.
Resultado:
Tiempo de ejecución:
fuente
Puede convertir la lista a un conjunto y usar la función de intersección para verificar la superposición:
(solo se utiliza 1 función de aplicación como solicitó :-))
fuente
Solía
product
obtener todas las combinaciones. Luego podemos verificar connumpy.isin
ynumpy.mean
:Muestra de tiempo
fuente
Debe ser rápido, también considere el duplicado en la lista
fuente
¡Si! Estamos buscando un producto cartesiano aquí, que se proporciona en esta respuesta. Esto se puede lograr sin un bucle for o una comprensión de la lista
Agreguemos un nuevo valor repetido a nuestro marco de datos
df
para que se vea así:Siguiente fusionarse consigo mismo
Así es como se ve el marco combinado:
Luego aplicamos la función deseada a cada fila usando
axis=1
Reformar esto para obtener valores en el formato deseado
Espero que esto ayude :)
fuente