问题描述
我正在尝试使用 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