【问题标题】:Python Pandas: How to merge based on an "OR" condition?Python Pandas:如何根据“OR”条件进行合并?
【发布时间】:2018-02-02 19:47:06
【问题描述】:

假设我有两个数据框,两者的列名是:

table 1 columns:
[ShipNumber, TrackNumber, ShipDate, Quantity, Weight]
table 2 columns:
[ShipNumber, TrackNumber, AmountReceived]

我想根据 ShipNumber 和 TrackNumber 合并这两个表。 但是,如果我只是按以下方式使用合并(伪代码,而不是真实代码):

tab1.merge(tab2, "left", on=['ShipNumber','TrackNumber'])

那么,这意味着两个表中 ShipNumber 和 TrackNumber 列中的值必须匹配。

但是,在我的例子中,有时 ShipNumber 列的值会匹配,有时 TrackNumber 列的值会匹配; 只要两个值之一匹配一行,我希望合并发生。

也就是说,如果 tab 1 中的 row 1 ShipNumber 匹配 tab 2 中的 row 3 ShipNumber,但是两条记录的两个表中的 TrackNumber 不匹配,我仍然想匹配两个表中的两行。

所以基本上这是一个非此即彼的匹配条件(伪代码):

if tab1.ShipNumber == tab2.ShipNumber OR tab1.TrackNumber == tab2.TrackNumber:
    then merge

我希望我的问题是有道理的... 非常感谢任何帮助!

按照建议,我查看了这篇文章: Python pandas merge with OR logic 但我认为这不是完全相同的问题,因为该帖子中的 OP 有一个映射文件,因此他们可以简单地进行 2 次合并来解决这个问题。但我没有映射文件,而是有两个具有相同键列的 df(ShipNumber、TrackNumber)

【问题讨论】:

  • 你看到这个帖子了吗:stackoverflow.com/questions/43925603/…
  • 其实那个帖子并没有解决这里的问题..
  • 嗨@Vico 我编辑了我的帖子以包含您建议的帖子,但我认为该帖子没有回答我的问题:(
  • 您的输出中总共需要四列吗? IE。 A_tab1, A_tab2, B_tab1, B_tab2?如果只有 AB 需要在任何给定行上匹配,您似乎将需要所有这些。
  • 无论如何,如果没有您的数据或预期的输出,就不能说更多。

标签: python pandas dataframe merge


【解决方案1】:

使用merge()concat()。然后删除AB 匹配的所有重复案例(感谢@Scott Boston 最后一步)。

df1 = pd.DataFrame({'A':[3,2,1,4], 'B':[7,8,9,5]})
df2 = pd.DataFrame({'A':[1,5,6,4], 'B':[4,1,8,5]})

df1         df2
   A  B        A  B
0  3  7     0  1  4
1  2  8     1  5  1
2  1  9     2  6  8
3  4  5     3  4  5

有了这些数据框,我们应该可以看到:

  • df1.loc[0]A 匹配 df2.loc[0]
  • df1.loc[1]B 匹配 df2.loc[2]
  • df1.loc[3]df2.loc[3] 上同时匹配AB

我们将使用后缀来跟踪匹配的位置:

suff_A = ['_on_A_match_1', '_on_A_match_2']
suff_B = ['_on_B_match_1', '_on_B_match_2']

df = pd.concat([df1.merge(df2, on='A', suffixes=suff_A), 
                df1.merge(df2, on='B', suffixes=suff_B)])

     A  A_on_B_match_1  A_on_B_match_2    B  B_on_A_match_1  B_on_A_match_2
0  1.0             NaN             NaN  NaN             9.0             4.0
1  4.0             NaN             NaN  NaN             5.0             5.0
0  NaN             2.0             6.0  8.0             NaN             NaN
1  NaN             4.0             4.0  5.0             NaN             NaN

请注意,第二行和第四行是重复匹配项(对于两个数据帧,A = 4B = 5)。我们需要删除其中一组。

dups = (df.B_on_A_match_1 == df.B_on_A_match_2) # also could remove A_on_B_match
df.loc[~dups]

     A  A_on_B_match_1  A_on_B_match_2    B  B_on_A_match_1  B_on_A_match_2
0  1.0             NaN             NaN  NaN             9.0             4.0
0  NaN             2.0             6.0  8.0             NaN             NaN
1  NaN             4.0             4.0  5.0             NaN             NaN

【讨论】:

  • 我认为您在 concat 之后需要 drop_duplicates 以确保您的消除记录可能在 A 和 B 上都成功加入。
  • 好收获!我将使用示例数据中的边缘情况进行更新。
  • 这种情况下 concat 和 append 是一样的吗?
  • 是的,您也可以使用df1.merge(on='A').append(df1.merge(on='B'))。我认为concat 通常更快。
  • @andrew_reece 你能解释一下为什么你的代码中只有一个参数“on”用于合并 df1.merge(on='A').append(df1.merge(on='B' ))?这是什么意思?
【解决方案2】:

我会建议这种替代方式来进行这样的合并。这对我来说似乎更容易。

table1["id_to_be_merged"] = table1.apply(
    lambda row: row["ShipNumber"] if pd.notnull(row["ShipNumber"]) else row["TrackNumber"], axis=1)

如果需要,您也可以在table2 中添加相同的列,然后根据您的要求在left_inright_on 中使用。

【讨论】:

  • applylambdas 不适合初学者,因此,由于缺乏解释,答案可能没有那么有用。您还可以使您的代码更具可读性。它在性能上是否超过了上述方法?
  • 我认为这不能解决问题。我认为提问者的大多数行都包括有效的船号和有效的轨道号。此外,提问者不希望将表 1 的船号与表 2 的轨道号相匹配,反之亦然。它必须是 (table_1.ShipNumber == table2.ShipNumber) or (table_1.TrackNumber == table2.TrackNumber) 。匹配table_1.ShipNumber == table_2.TrackNumber 会是个问题。
猜你喜欢
  • 2020-07-04
  • 2021-10-31
  • 2018-02-13
  • 2022-11-26
  • 2017-08-14
  • 2013-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多