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_dummiespara 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,
0y1es solo su producto de punto, porque un personaje pertenece a ambas filas si y solo si está representado por1ambas.Con eso en mente, primer uso
para convertir cada celda en una serie y concatenar todas esas series. Salida:
Ahora, usamos
pd.get_dummiesesa 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 12kmarco 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 laintersectionoperació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_valueEn caso de que haya valores duplicados en cada lista, debe usarlos en
collections.Counterlugar 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
pandascon 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
valuecolumna. 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
listdecir, sustrointo 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
productobtener todas las combinaciones. Luego podemos verificar connumpy.isinynumpy.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
dfpara 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=1Reformar esto para obtener valores en el formato deseado
Espero que esto ayude :)
fuente