Imagine que tengo un marco de datos, df, que tiene 2 columnas, un USER_ID y un PRODUCTO que compraron.

df
USER_ID     |     PRODUCT
1                 a
1                 b
1                 c
2                 d
2                 a
2                 k

Quiero convertir este DataFrame en uno nuevo, df2, donde cada fila es un usuario y los productos se agregan en una lista de cadenas.

df2
USER_ID     |     PRODUCT
1                 [a,b,c]
2                 [d,a,k]

Finalmente, me gustaría poder encontrar la intersección entre las listas de PRODUCTOS de dos usuarios.

Puedo crear el segundo marco de datos, pero el método que estoy usando da como resultado una lista que de alguna manera no es iterable.

Específicamente lo hago: df2 = df1.groupby (‘USER_ID) [‘ PRODUCT ']. Agg (lambda x: x.tolist ())

Lo que me da una serie que vuelvo a convertir en un marco de datos.

df2 = df2.to_frame()

Lo que me da el df2 que estoy buscando, pero cada lista de productos tiene una longitud = 1, por lo que no puedo comparar 1 con otro para encontrar la intersección de productos. Por ejemplo, cuando ejecuto:

s1 = df2.PRODUCT[df2.USER_ID == 1] 
s2 = df2.PRODUCT[df2.USER_ID == 2]

common_elements = list(set(s1).intersection(set(s2)))
common_elements

El resultado es una lista vacía en lugar de [a]. ¿Qué estoy haciendo mal?

1
James Eaves 7 jun. 2016 a las 20:33

4 respuestas

La mejor respuesta

¿Es eso lo que quieres?

In [7]: pd.Series(np.intersect1d(df.loc[df.USER_ID == 1, 'PRODUCT'], df.loc[df.USER_ID == 2, 'PRODUCT']))
Out[7]:
0    a
dtype: object

O usando index.intersection ():

In [18]: (df.set_index('PRODUCT').query('USER_ID == 1').index
   ....:    .intersection(df.set_index('PRODUCT').query('USER_ID == 2').index)
   ....:    .to_series()
   ....: )
Out[18]:
PRODUCT
a    a
Name: PRODUCT, dtype: object

PD: no convertiría tu df en df2 ya que probablemente tendrás muchas dificultades con este modelo de datos (es decir, tener listas en columnas)

0
MaxU 7 jun. 2016 a las 18:14

Esto le dará una solución generalizable para encontrar la intersección de cualquier lista de productos de dos usuarios sin el segundo marco de datos descuidado

from collections import defaultdict

user1 = 1
user2 = 2
products = defaultdict(set)

for record in df.to_dict('records'):
    products[record['USER_ID']].add(record['PRODUCT'])

common_elements = products[user1].intersection(products[user2])]
print(common_elements)

Y luego, si quisieras todas las intersecciones con todos los pares de usuarios

from itertools import combinations
common_elements = {(x,y): products[x].intersection(products[y]) for x,y in combinations(products.keys(),2)}
0
michael_j_ward 7 jun. 2016 a las 19:15

Puede realizar groupby seguido de encontrar la intersección entre las dos listas como se muestra:

>>>df2 = df.groupby('USER_ID')['PRODUCT'].apply(list).reset_index()
>>>df2

   USER_ID    PRODUCT
0        1  [a, b, c]
1        2  [d, a, k]

>>>list(set(df2['PRODUCT'].loc[0]).intersection(df2['PRODUCT'].loc[1]))
['a']

O de una manera más corta:

df2 = df.groupby('USER_ID')['PRODUCT'].apply(list)
>>>list(set(df2.loc[1]).intersection(df2.loc[2]))
['a']
1
Nickil Maveli 2 feb. 2017 a las 11:02

Prueba esto:

df3 = pd.crosstab(df2.PRODUCT,df2.USER_ID, margins= True)
print df3[df3['All']>1]

  # USER_ID  1  2  All
   # PRODUCT           
   # a        1  1    2
   # All      3  3    6

Mi solución es muy similar a @Nikil, así que usa la suya.

df2 = df.groupby('USER_ID')['PRODUCT'].apply(list)
df2 = df2.reset_index()
print df2


#         USER_ID    PRODUCT
#    0        1  [a, b, c]
#    1        2  [d, a, k]

Más información sobre la tabla de referencias cruzadas, es un marco de datos.

pd.crosstab(df2.PRODUCT,df2.USER_ID, margins= True)

#    USER_ID  1  2  All
#    PRODUCT           
#    a        1  1    2
#    b        1  0    1
#    c        1  0    1
#    d        0  1    1
#    k        0  1    1
#    All      3  3    6
1
Merlin 7 jun. 2016 a las 19:22