【问题标题】:pandas true outer join?熊猫真正的外加入?
【发布时间】:2021-02-10 07:39:31
【问题描述】:

如何在 pandas 中获得真正的外连接?这意味着它实际上为您提供了整个输出,而不是组合要合并的列。在我看来,这有点愚蠢,因为这样就很难确定要连续执行哪种操作。我一直这样做是为了检测是否应该插入、更新或删除数据,但是我总是必须在列上创建额外的合并副本,这对某些数据集来说只是一堆开销(有时是大量的)。

示例:

import pandas as pd

keys = ["A","B"]

df1 = pd.DataFrame({"A":[1,2,3],"B":["one","two","three"],"C":["testThis","testThat", "testThis"],"D":[None,hash("B"),hash("C")]})
df2 = pd.DataFrame({"A":[2,3,4],"B":["two","three","four"],"C":["testThis","testThat", "testThis"], "D":[hash("G"),hash("C"),hash("D")]})

fullJoinDf = df1.merge(df2, how="outer", left_on=keys, right_on=keys, suffixes=["","_r"])
display(
    fullJoinDf,
)

    A   B       C           D               C_r          D_r
0   1   one     testThis    NaN             NaN          NaN
1   2   two     testThat    -3.656526e+18   testThis    -9.136326e+18
2   3   three   testThis    -8.571400e+18   testThat    -8.571400e+18
3   4   four    NaN         NaN             testThis    -4.190116e+17

注意到它如何输出AB 神奇地组合到一组列。我想要的是我会在 SQL 外连接等中得到什么:

    A    B      C           D               A_r  B_r     C_r        D_r
0   1    one    testThis    NaN             NaN  NaN     NaN        NaN     
1   2    two    testThat    -3.656526e+18   2    two     testThis   -9.136326e+18
2   3    three  testThis    -8.571400e+18   3    three   testThat   -8.571400e+18
3   NaN  NaN    NaN         NaN             4    four    testThis   -4.190116e+17

为@Felipe Whitaker 编辑

使用连接:

df3 = df1.copy().set_index(keys)
df4 = df2.copy().set_index(keys)
t = pd.concat([df3,df4], axis=1)
t.reset_index(), 

    A   B       C           D               C           D
0   1   one     testThis    NaN             NaN         NaN
1   2   two     testThat    -3.656526e+18   testThis    -9.136326e+18
2   3   three   testThis    -8.571400e+18   testThat    -8.571400e+18
3   4   four    NaN         NaN             testThis    -4.190116e+17

编辑示例* 鉴于答案,我将发布更多测试,因此任何偶然发现此问题的人都可以看到我在执行此操作时发现的更多“gatcha”变体。

import pandas as pd

keys = ["A","B"]

df1 = pd.DataFrame({"A":[1,2,3],"B":["one","two","three"],"C":["testThis","testThat", "testThis"],"D":[None,hash("B"),hash("C")]})
df2 = pd.DataFrame({"A":[2,3,4],"B":["two","three","four"],"C":["testThis","testThat", "testThis"], "D":[hash("G"),hash("C"),hash("D")]})

df3 = df1.copy()
df4 = df2.copy()
df3.index = df3[keys]
df4.index = df4[keys]

df5 = df1.copy().set_index(keys)
df6 = df2.copy().set_index(keys)


fullJoinDf = df5.merge(df6, how="outer", left_on=keys, right_on=keys, suffixes=["","_r"])
fullJoinDf_2 = df3.merge(df4, how="outer", left_index=True, right_index=True, suffixes=["","_r"])
t = pd.concat([df1,df2], axis=1, keys=["A","B"])
display(
    df3.index,
    df5.index,
    fullJoinDf,
    fullJoinDf_2,
    t,
)

Index([(1, 'one'), (2, 'two'), (3, 'three')], dtype='object')
MultiIndex([(1,   'one'),
            (2,   'two'),
            (3, 'three')],
           names=['A', 'B'])

    A   B       C           D               C_r         D_r
0   1   one     testThis    NaN             NaN         NaN
1   2   two     testThat    -3.656526e+18   testThis    -9.136326e+18
2   3   three   testThis    -8.571400e+18   testThat    -8.571400e+18
3   4   four    NaN         NaN             testThis    -4.190116e+17

            A    B      C           D               A_r  B_r    C_r        D_r
(1, one)    1.0  one    testThis    NaN             NaN  NaN    NaN        NaN
(2, two)    2.0  two    testThat    -3.656526e+18   2.0  two    testThis    -9.136326e+18
(3, three)  3.0  three  testThis    -8.571400e+18   3.0  three  testThat    -8.571400e+18
(4, four)   NaN  NaN    NaN         NaN             4.0  four   testThis    -4.190116e+17

    A   B       C           D               A   B       C           D
0   1   one     testThis    NaN             2   two     testThis    -9136325526401183790
1   2   two     testThat    -3.656526e+18   3   three   testThat    -8571400026927442160
2   3   three   testThis    -8.571400e+18   4   four    testThis    -419011572131270498

【问题讨论】:

  • 你为什么不用pd.concat(iter, axis = 1)?
  • @FelipeWhitaker - concat 似乎做同样的事情,请参阅编辑。
  • 我真的认为它只是将它们连接起来。这个结果是反直觉的。好的,谢谢。
  • 你不是要求按列连接,而不是外连接(如果至少有公共列,它只是一个连接,对吧?)?我无法理解您的示例,请编辑以澄清您的意思 “我认为这有点愚蠢,因为这样很难确定要连续执行哪种操作。”?这不是我们通常设计模式以在数据库表中具有一些主键(/id)的原因吗?如果没有,我们如何理解您的数据?
  • 我看不出您的架构从具有不同不兼容 ID 的两列 "A":[1,2,3],"B":["one","two","three"] 中获得了什么,如果您打算将其与连接和合并一起使用,它对我来说只是一个糟糕的架构设计。

标签: python pandas concat outer-join


【解决方案1】:

如果您根本不关心原始索引:

df1.index = df1[keys]
df2.index = df2[keys]

fullJoinDf = df1.merge(df2, how="outer", left_index=True, right_index=True, suffixes=["","_r"])

结果:

     A      B         C             D  A_r    B_r       C_r           D_r
0  1.0    one  testThis           NaN  NaN    NaN       NaN           NaN
1  2.0    two  testThat  6.368540e+18  2.0    two  testThis -6.457388e+18
2  3.0  three  testThis -7.490461e+18  3.0  three  testThat -7.490461e+18
3  NaN    NaN       NaN           NaN  4.0   four  testThis  4.344649e+18

【讨论】:

  • df1.index = df1[keys] 只是伪装的df1.set_index(['A','B'], inplace=True, drop=False),这本来应该在数据帧上完成。
  • @smci - 您的评论不正确。 df1.index = df1[keys],返回与 df1.set_index(['A','B']) 不同的结果。这是问题的全部症结所在,也是为什么我上面的所有示例都不起作用,但这个答案却起作用。
  • @JamieMarshall:这是使用元组列表作为索引而不是 pandas MultiIndex。实际上,以非熊猫的方式混合索引列“A”和“B”的等价性似乎是一个 XY 问题。这似乎不是一个好习惯,你能保证 pandas 可以处理或不会在任何进一步的操作中破坏这个数据帧吗? (加入、合并、reset_index 等 reset_index() 不起作用)
  • @smci - 底线,set_index 不起作用。我看不出有什么办法可以解决这个问题,除非你可以阻止set_index 吃掉索引列。我不能。
  • @JamieMarshall:我已经向您展示了df1.set_index(['A','B'], inplace=True, drop=False) 可以防止熊猫吃掉索引列。您错过了关键的 drop=False 部分。还有inplace=True 以防止不必要的复制。我认为这就是您想要的;如果不是,请具体说明原因。
【解决方案2】:

如果您在 merge 之前的 1 个 DataFrames 中重命名合并中使用的列,它看起来会给出正确的答案

df1.merge(df2.rename({'A': 'A_y', 'B': 'B_y'}, axis =1), left_on=keys, right_on=['A_y', 'B_y'], how='outer')
#output:
    A   B       C_x         D_x             A_y     B_y     C_y         D_y
0   1.0 one     testThis    NaN             NaN     NaN     NaN         NaN
1   2.0 two     testThat    -2.482945e+18   2.0     two     testThis    -1.215774e+18
2   3.0 three   testThis    1.140152e+17    3.0     three   testThat    1.140152e+17
3   NaN NaN     NaN         NaN             4.0     four    testThis    -4.915382e+18

【讨论】:

  • 我将@CodeDifferent 标记为正确的,但这可能是更快的操作。设置索引需要孔列,要重新设置,这可能效率更高 需要测试才能找到。
猜你喜欢
  • 1970-01-01
  • 2016-11-25
  • 1970-01-01
  • 2014-09-21
  • 2020-12-06
  • 2018-11-26
  • 1970-01-01
  • 2017-03-28
  • 1970-01-01
相关资源
最近更新 更多