`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 上的 an article 中,建议使用 axis=1。但是,我认为这是错误的:

>>> pd.concat([df1,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

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

解决方法

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

方法 #1 (concat):我认为最动态、最健壮的 pd.concat 方式(我专门尝试使用 concat 的方式)是使用。与下面的第二种方法相比,此解决方案的唯一主要好处是您不必使用额外的库;但是,我认为您也可以使用 merge 编写类似的代码,而无需使用其他库:

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:(merge with reduce):

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

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

如果您正在尝试查找公共行:

temp = pd.concat([df1,df3])
temp[temp.duplicated()]

不过,我相信有一个更优雅的解决方案。

,

试试这个,

df = pd.merge(df1,how='inner',on=[col1,col2,col3])
,
# add extral tag column
df_list = [df1,df3]
for i,dfi in enumerate(df_list):
    dfi['tag'] = i + 1

# merge DataFrame
df = pd.concat([df1,df3],ignore_index=True)

# find the duplicates rows
cols = df.columns[:-1].tolist()
cond = df[cols].duplicated(keep=False)
obj = df[cond].groupby(cols)['tag'].agg(tuple)

# filter 
cond = obj.map(len) == len(df_list)
obj[cond]

obj 示例:

# 0         1         2       
# 0.148080  0.837398  0.565498       (1,3)
# 0.572673  0.256735  0.620923    (1,2,3)
# 0.822542  0.856137  0.645639       (1,2)
# Name: tag,dtype: object
,

与@Ajay A 所说的类似,

geometry

那么,

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 Out[22]: 0 1 2 0 0.845894 0.530659 0.629198 1 0.697229 0.225557 0.314540 2 0.972633 0.685077 0.191109 3 0.069966 0.961317 0.352933 4 0.176633 0.663602 0.235032 df2 Out[23]: 0 1 2 0 0.697229 0.225557 0.314540 1 0.972633 0.685077 0.191109 df3 Out[24]: 0 1 2 0 0.972633 0.685077 0.191109 1 0.069966 0.961317 0.352933 pd.merge 一起使用

how='inner'

或者如果您正在寻找的是三者的交集,

pd.merge(df2,df3,how='inner')
Out[25]: 
          0         1         2
0  0.972633  0.685077  0.191109

使用 pd.merge(pd.merge(df1,how='inner'),how='inner') Out[26]: 0 1 2 0 0.972633 0.685077 0.191109 处理 for loop

df_list