web-dev-qa-db-fra.com

Recherche de lignes communes (intersection) dans deux Pandas dataframes

Supposons que j'ai deux images de données de ce format (appelez-les df1 et df2):

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

Je cherche à obtenir un cadre de données de toutes les lignes qui ont un commun user_id dans df1 et df2. (ie si un user_id est dans les deux df1 et df2, inclure les deux lignes dans la trame de données en sortie)

Je peux penser à beaucoup de façons d'aborder cela, mais elles me paraissent toutes maladroites. Par exemple, nous pourrions trouver l’unique user_ids dans chaque cadre de données, créez un ensemble de chacun, trouvez leur intersection, filtrez les deux images avec l’ensemble obtenu et concaténez les deux images filtrées.

C’est peut-être la meilleure approche, mais je sais que Pandas est intelligent. Existe-t-il un moyen plus simple de procéder? J'ai jeté un œil sur merge mais je ne pense pas que ce soit ce J'ai besoin de.

39
David Chouinard

Si j'ai bien compris, il est préférable de répondre à cette question dans this post .

Mais brièvement, la réponse au PO avec cette méthode est simplement:

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

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

58
aldorath

Si je vous ai bien compris, 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

Il s’agit essentiellement de l’algorithme que vous avez qualifié de "maladroit", utilisant les méthodes idiomatiques pandas. Notez les index de lignes en double. Notez également que cela ne vous donnera pas le résultat attendu si df1 et df2 n’a pas d’indices de rangées qui se chevauchent, c’est-à-dire si

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

En fait, cela ne donnera pas la sortie attendue si leurs index de lignes ne sont pas égaux.

11
Phillip Cloud

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 rejoindre et puis unpivot (possible dans 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

La seconde pourrait être écrite en 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
Roman Pekar