首页 > 解决方案 > `pd.concat` 与 `join=='inner'` 不会产生 pandas 数据帧的交集

问题描述

我正在尝试使用 pd.concat 从多个数据帧中提取公共行:

>>> import numpy as np
>>> import pandas as pd
>>> x = np.random.random(size=(5, 3))
>>> df1 = pd.DataFrame(x)
>>> df2 = pd.DataFrame(x[1:3])
>>> df3 = pd.DataFrame(x[2:4])
>>> df1
          0         1         2
0  0.257662  0.453542  0.805230
1  0.060493  0.463148  0.715994
2  0.452379  0.470137  0.965709
3  0.447546  0.964252  0.163247
4  0.187212  0.973557  0.871090
>>> df2
          0         1         2
0  0.060493  0.463148  0.715994
1  0.452379  0.470137  0.965709
>>> df3
          0         1         2
0  0.452379  0.470137  0.965709
1  0.447546  0.964252  0.163247

如您所见,只有该行0.452379 0.470137 0.965709对所有三个数据框都是通用的。为了提取它,我尝试了:

>>> pd.concat([df1, df2, df3], join='inner')
          0         1         2
0  0.257662  0.453542  0.805230
1  0.060493  0.463148  0.715994
2  0.452379  0.470137  0.965709
3  0.447546  0.964252  0.163247
4  0.187212  0.973557  0.871090
0  0.060493  0.463148  0.715994
1  0.452379  0.470137  0.965709
0  0.452379  0.470137  0.965709
1  0.447546  0.964252  0.163247

因此,join==inner似乎不起作用!我还应该指出,这ignore_index=True对行为没有影响。在一篇关于 Real Python 的文章中,axis=1建议使用。但是,我认为这是错误的:

>>> pd.concat([df1, df2, df3], join='inner', axis=1)
          0         1         2         0         1         2         0         1         2
0  0.257662  0.453542  0.805230  0.060493  0.463148  0.715994  0.452379  0.470137  0.965709
1  0.060493  0.463148  0.715994  0.452379  0.470137  0.965709  0.447546  0.964252  0.163247

我在做什么有什么问题?另外,如果这种方式不起作用,我将如何从多个数据框中提取公共行?我使用的是熊猫版本 0.25.3。

标签: pythonpython-3.xpandasdataframeset-intersection

解决方案


简而言之,使用reduce(lambda left,right: pd.merge(left,right,on=cols), dfs), (请参阅方法 #2 - 确保包含from functools import reduce),但请参阅pd.concat(方法 #1)的说明:

方法#1 ( ):我认为(在我专门尝试过的方法中concat)最有活力、最稳健的方法是使用。此解决方案相对于下面第二种方法的唯一主要好处是您不必使用额外的库;但是,我认为您也可以编写类似的代码,而无需使用其他库:pd.concatconcatmerge

dfs = [df1, df2, df3]
cols = [*df1.columns]                              # enclosing with [*] is the same as tolist()
for df in dfs:
    df.set_index(cols, inplace=True)               # can only use inplace when looping through dfs (at least using my simpler method)
pd.concat(dfs, join='inner', axis=1).reset_index() # see below paragraph for explanation
Out[1]: 
          0         1         2
0  0.452379  0.470137  0.965709

请注意,这join='inner'意味着您加入的index不是唯一的行。此外,join只有通过 才重要axis=1,这就是为什么实际上什么都没有发生的原因。


方法 #2 : (mergereduce):

@Anky 指出这how='inner'是默认的merge. 这实际上是我发布的第一个答案,但我对预期的输出感到困惑并绕了一圈。请看下面最简单的答案:

from functools import reduce
dfs = [df1, df2, df3]
cols = [*df1.columns]
reduce(lambda left,right: pd.merge(left,right,on=cols), dfs)
Out[2]: 
          0         1         2
0  0.452379  0.470137  0.965709

推荐阅读