Encontrar filas comunes (intersección) en dos marcos de datos Pandas


Supongamos que tengo dos dataframes de este formato (llámelos df1 y df2):

+------------------------+------------------------+--------+
|        user_id         |      business_id       | rating |
+------------------------+------------------------+--------+
| rLtl8ZkDX5vH5nAx9C3q5Q | eIxSLxzIlfExI6vgAbn2JA |      4 |
| C6IOtaaYdLIT5fWd7ZYIuA | eIxSLxzIlfExI6vgAbn2JA |      5 |
| mlBC3pN9GXlUUfQi1qBBZA | KoIRdcIfh3XWxiCeV1BDmA |      3 |
+------------------------+------------------------+--------+

Estoy buscando un dataframe de todas las filas que tienen un user_id común en df1 y df2. (IE. si user_id está en df1 y df2, incluya las dos filas en el dataframe de salida)

Se me ocurren muchas maneras de abordar esto, pero todas me parecen torpes. Por ejemplo, podríamos encontrar todos los user_idúnicos en cada dataframe, crear un conjunto de cada uno, encontrar su intersección, filtrar los dos dataframes con el conjunto resultante y concatenar los dos dataframes filtrados.

Tal vez ese sea el mejor enfoque, pero sé que Pandas es inteligente. ¿Hay una forma más sencilla de hacer esto? He mirado merge pero no creo que eso sea lo que necesito.

Author: David Chouinard, 2013-10-27

3 answers

Mi entendimiento es que esta pregunta es mejor respondida en este post.

Pero brevemente, la respuesta al OP con este método es simplemente:

s1 = pd.merge(df1, df2, how='inner', on=['user_id'])

Que da s1 con 5 columnas: user_id y las otras dos columnas de cada uno de df1 y df2.

 50
Author: aldorath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:53

Si te entiendo correctamente, puedes usar una combinación de Series.isin() y DataFrame.append():

In [80]: df1
Out[80]:
   rating  user_id
0       2  0x21abL
1       1  0x21abL
2       1   0xdafL
3       0  0x21abL
4       4  0x1d14L
5       2  0x21abL
6       1  0x21abL
7       0   0xdafL
8       4  0x1d14L
9       1  0x21abL

In [81]: df2
Out[81]:
   rating      user_id
0       2      0x1d14L
1       1    0xdbdcad7
2       1      0x21abL
3       3      0x21abL
4       3      0x21abL
5       1  0x5734a81e2
6       2      0x1d14L
7       0       0xdafL
8       0      0x1d14L
9       4  0x5734a81e2

In [82]: ind = df2.user_id.isin(df1.user_id) & df1.user_id.isin(df2.user_id)

In [83]: ind
Out[83]:
0     True
1    False
2     True
3     True
4     True
5    False
6     True
7     True
8     True
9    False
Name: user_id, dtype: bool

In [84]: df1[ind].append(df2[ind])
Out[84]:
   rating  user_id
0       2  0x21abL
2       1   0xdafL
3       0  0x21abL
4       4  0x1d14L
6       1  0x21abL
7       0   0xdafL
8       4  0x1d14L
0       2  0x1d14L
2       1  0x21abL
3       3  0x21abL
4       3  0x21abL
6       2  0x1d14L
7       0   0xdafL
8       0  0x1d14L

Este es esencialmente el algoritmo que describiste como "torpe", usando métodos idiomáticos pandas. Observe los índices de fila duplicados. Además, tenga en cuenta que esto no le dará la salida esperada si df1 y df2 no tienen índices de fila superpuestos, es decir, si

In [93]: df1.index & df2.index
Out[93]: Int64Index([], dtype='int64')

De hecho, no dará la salida esperada si sus índices de fila no son iguales.

 9
Author: Phillip Cloud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-27 15:01:29

En SQL, este problema podría ser resuelto por varios métodos:

select * from df1 where exists (select * from df2 where df2.user_id = df1.user_id)
union all
select * from df2 where exists (select * from df1 where df1.user_id = df2.user_id)

O join y luego unpivot (posible en SQL server)

select
    df1.user_id,
    c.rating
from df1
    inner join df2 on df2.user_i = df1.user_id
    outer apply (
        select df1.rating union all
        select df2.rating
    ) as c

El segundo podría escribirse en pandas con algo como:

>>> df1 = pd.DataFrame({"user_id":[1,2,3], "rating":[10, 15, 20]})
>>> df2 = pd.DataFrame({"user_id":[3,4,5], "rating":[30, 35, 40]})
>>>
>>> df4 = df[['user_id', 'rating_1']].rename(columns={'rating_1':'rating'})
>>> df = pd.merge(df1, df2, on='user_id', suffixes=['_1', '_2'])
>>> df3 = df[['user_id', 'rating_1']].rename(columns={'rating_1':'rating'})
>>> df4 = df[['user_id', 'rating_2']].rename(columns={'rating_2':'rating'})
>>> pd.concat([df3, df4], axis=0)
   user_id  rating
0        3      20
0        3      30
 3
Author: Roman Pekar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-27 14:57:15