Trouver des lignes communes (intersection) dans deux DataFrames de Pandas

supposons que j'ai deux images de ce format (appelez-les df1 et df2 ):

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

je cherche une base de données de toutes les lignes qui ont un user_id commun dans df1 et df2 . (IE. si un user_id est à la fois dans df1 et df2 , inclure les deux lignes dans la base de données de sortie)

je peux penser à beaucoup de façons d'aborder cela, mais ils me semblent tous comme clunky. Exemple, nous avons pu trouver tous les user_id uniques dans chaque dataframe, créer un ensemble de chacun, trouver leur intersection, filtrer les deux dataframes avec le résultat et concaténer les deux dataframes filtrées.

C'est peut-être la meilleure approche, mais je sais que Pandas est intelligent. Est-il un moyen plus simple de faire cela? J'ai regardé merge mais je ne pense pas que c'est ce dont j'ai besoin.

33
demandé sur David Chouinard 2013-10-27 18:03:18

3 réponses

ma compréhension est que cette question est mieux répondu dans ce post .

mais brièvement, la réponse à L'OP avec cette méthode est simplement:

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

qui donne s1 avec 5 colonnes: user_id et les deux autres colonnes de df1 et df2.

50
répondu aldorath 2017-05-23 11:54:53

Si je vous comprends bien, vous pouvez utiliser une combinaison de Series.isin() et 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

c'est essentiellement l'algorithme que vous avez décrit comme" clunky", en utilisant les méthodes idiomatiques pandas . Notez les indices de ligne dupliqués. En outre, notez que cela ne vous donnera pas le résultat attendu si df1 et df2 n'ont pas d'indices de ligne se chevauchant, i.e., si

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

en fait, il ne donnera pas le résultat attendu si leurs indices ne sont pas égaux.

9
répondu Phillip Cloud 2013-10-27 15:01:29

en SQL, ce problème pourrait être résolu par plusieurs méthodes:

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)

ou à les rejoindre, puis unpivot (possible 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

Second pourrait être écrites dans les pandas avec quelque chose comme:

>>> 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
répondu Roman Pekar 2013-10-27 14:57:15