【问题标题】:Finding common rows (intersection) in two Pandas dataframes在两个 Pandas 数据框中查找公共行(交集)
【发布时间】:2022-05-10 23:10:41
【问题描述】:

假设我有两个这种格式的数据帧(称它们为df1df2):

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

我正在寻找在df1df2 中具有共同user_id 的所有行的数据框。 (即如果user_iddf1df2 中,则在输出数据帧中包含这两行)

我可以想出很多方法来解决这个问题,但它们都让我觉得笨拙。例如,我们可以在每个数据帧中找到所有唯一的user_ids,创建一个集合,找到它们的交集,用结果集过滤两个数据帧并将两个过滤后的数据帧连接起来。

也许这是最好的方法,但我知道 Pandas 很聪明。有没有更简单的方法来做到这一点?我看过merge,但我认为这不是我需要的。

【问题讨论】:

    标签: python pandas dataframe intersect


    【解决方案1】:

    我的理解是,这个问题最好在this post 中回答。

    但简而言之,用这种方法对 OP 的答案很简单:

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

    这给了s1 5 列:user_id 以及来自df1df2 的另外两列。

    【讨论】:

      【解决方案2】:

      如果我理解正确,您可以使用Series.isin()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
      

      这本质上是您描述为“笨拙”的算法,使用惯用的pandas 方法。注意重复的行索引。另外,请注意,如果 df1df2 没有重叠的行索引,即如果

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

      事实上,如果它们的行索引不相等,它不会给出预期的输出。

      【讨论】:

      • 啊。我曾想过,但它并没有给我想要的东西。我希望将这两行作为输出数据框中的两个单独的行。该解决方案将列数加倍并使用前缀。我认为没有办法使用merge 创建两个单独的行。
      • +1 用于合并,但看起来 OP 想要一些不同的输出。我已经创建了他需要的东西,但我不确定它是最优雅的 pandas 解决方案
      • 这只有在df1df2 的长度相同时才有效。否则这个df2.user_id.isin(df1.user_id) & df1.user_id.isin(df2.user_id)将无法一起广播。
      • 我认为使用df1 = df1[df1.index.isin(df2.index)]; df2 = df2[df2.index.isin(df1.index)]可以解决
      • 这里为什么需要 &? df2.user_id.isin(df1.user_id) & df1.user_id.isin(df2.user_id)
      【解决方案3】:

      在 SQL 中,这个问题可以通过以下几种方法来解决:

      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)
      

      或加入然后反透视(可能在 SQL 服务器中)

      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
      

      第二个可以用 pandas 写成:

      >>> 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
      

      【讨论】:

      • 第二种方法可以优化/缩短吗?
      猜你喜欢
      • 1970-01-01
      • 2016-12-05
      • 1970-01-01
      • 1970-01-01
      • 2019-12-01
      • 2017-09-03
      • 2013-09-18
      相关资源
      最近更新 更多