Pandas三向连接列上的多个数据框架

我有3个CSV文件。每个数据框架的第一列都是人的(字符串)名,而每个数据框架中的所有其他列都是人的属性。

我如何将所有三个CSV文档“联接”在一起,以创建一个单一的CSV,其中每行都具有人的字符串名称的每个唯一值的所有属性?

pandas中的join()函数指定我需要一个多索引,但我对分层索引方案与基于单个索引进行连接有什么关系感到困惑。

474697 次浏览

如果你有3个数据框架,你可以试试这个

# Merge multiple dataframes
df1 = pd.DataFrame(np.array([
['a', 5, 9],
['b', 4, 61],
['c', 24, 9]]),
columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
['a', 5, 19],
['b', 14, 16],
['c', 4, 9]]),
columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
['a', 15, 49],
['b', 4, 36],
['c', 14, 9]]),
columns=['name', 'attr31', 'attr32'])


pd.merge(pd.merge(df1,df2,on='name'),df3,on='name')

或者,正如考兰德提到的

df1.merge(df2,on='name').merge(df3,on='name')
不需要多索引来执行加入操作。 我们只需要正确地设置要执行连接操作的索引列(例如df.set_index('Name')命令)

join操作默认在index上执行。 在你的例子中,你只需要指定Name列对应于你的索引。 下面是一个例子

教程可能有用。

# Simple example where dataframes index are the name on which to perform
# the join operations
import pandas as pd
import numpy as np
name = ['Sophia' ,'Emma' ,'Isabella' ,'Olivia' ,'Ava' ,'Emily' ,'Abigail' ,'Mia']
df1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=name)
df2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'],         index=name)
df3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'],     index=name)
df = df1.join(df2)
df = df.join(df3)


# If you have a 'Name' column that is not the index of your dataframe,
# one can set this column to be the index
# 1) Create a column 'Name' based on the previous index
df1['Name'] = df1.index
# 1) Select the index from column 'Name'
df1 = df1.set_index('Name')


# If indexes are different, one may have to play with parameter how
gf1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=range(8))
gf2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'], index=range(2,10))
gf3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'], index=range(4,12))


gf = gf1.join(gf2, how='outer')
gf = gf.join(gf3, how='outer')

零的回答基本上是一个reduce操作。如果我有很多数据框架,我会把它们放在一个这样的列表中(通过列表推导或循环或诸如此类的东西生成):

dfs = [df0, df1, df2, ..., dfN]

假设它们有一个共同的列,就像你的例子中的name一样,我将执行以下操作:

import functools as ft
df_final = ft.reduce(lambda left, right: pd.merge(left, right, on='name'), dfs)

这样,您的代码就可以处理您想合并的任何数量的数据框架。

对于数据帧列表df_list也可以这样做:

df = df_list[0]
for df_ in df_list[1:]:
df = df.merge(df_, on='join_col_name')

或者如果数据帧在生成器对象中(例如,为了减少内存消耗):

df = next(df_list)
for df_ in df_list:
df = df.merge(df_, on='join_col_name')

下面是一个合并数据帧字典的方法,同时保持列名与字典同步。如果需要,它还会填充缺失的值:

这是合并数据帧字典的函数

def MergeDfDict(dfDict, onCols, how='outer', naFill=None):
keys = dfDict.keys()
for i in range(len(keys)):
key = keys[i]
df0 = dfDict[key]
cols = list(df0.columns)
valueCols = list(filter(lambda x: x not in (onCols), cols))
df0 = df0[onCols + valueCols]
df0.columns = onCols + [(s + '_' + key) for s in valueCols]


if (i == 0):
outDf = df0
else:
outDf = pd.merge(outDf, df0, how=how, on=onCols)


if (naFill != None):
outDf = outDf.fillna(naFill)


return(outDf)

好的,让我们生成数据并测试:

def GenDf(size):
df = pd.DataFrame({'categ1':np.random.choice(a=['a', 'b', 'c', 'd', 'e'], size=size, replace=True),
'categ2':np.random.choice(a=['A', 'B'], size=size, replace=True),
'col1':np.random.uniform(low=0.0, high=100.0, size=size),
'col2':np.random.uniform(low=0.0, high=100.0, size=size)
})
df = df.sort_values(['categ2', 'categ1', 'col1', 'col2'])
return(df)




size = 5
dfDict = {'US':GenDf(size), 'IN':GenDf(size), 'GER':GenDf(size)}
MergeDfDict(dfDict=dfDict, onCols=['categ1', 'categ2'], how='outer', naFill=0)

这是join方法的理想情况

join方法正是为这些类型的情况而构建的。你可以加入任意数量的数据框架。调用的DataFrame与传递的DataFrame集合的索引连接。要使用多个dataframe,必须将连接列放在索引中。

代码看起来像这样:

filenames = ['fn1', 'fn2', 'fn3', 'fn4',....]
dfs = [pd.read_csv(filename, index_col=index_col) for filename in filenames)]
dfs[0].join(dfs[1:])

使用@zero的数据,你可以这样做:

df1 = pd.DataFrame(np.array([
['a', 5, 9],
['b', 4, 61],
['c', 24, 9]]),
columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
['a', 5, 19],
['b', 14, 16],
['c', 4, 9]]),
columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
['a', 15, 49],
['b', 4, 36],
['c', 14, 9]]),
columns=['name', 'attr31', 'attr32'])


dfs = [df1, df2, df3]
dfs = [df.set_index('name') for df in dfs]
dfs[0].join(dfs[1:])


attr11 attr12 attr21 attr22 attr31 attr32
name
a         5      9      5     19     15     49
b         4     61     14     16      4     36
c        24      9      4      9     14      9

还有来自熊猫的文档的另一个解决方案(我在这里没有看到),

使用.append

>>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
A  B
0  1  2
1  3  4
>>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
A  B
0  5  6
1  7  8
>>> df.append(df2, ignore_index=True)
A  B
0  1  2
1  3  4
2  5  6
3  7  8

ignore_index=True用于忽略附加数据帧的索引,将其替换为源数据帧中的下一个可用索引。

如果有不同的列名,将引入Nan

python 3.6.3和pandas 0.22.0中,你也可以使用concat,只要你将你想要用于连接的列设置为索引:

pd.concat(
objs=(iDF.set_index('name') for iDF in (df1, df2, df3)),
axis=1,
join='inner'
).reset_index()

其中df1df2df3定义为约翰·高尔特的回答:

import pandas as pd
df1 = pd.DataFrame(np.array([
['a', 5, 9],
['b', 4, 61],
['c', 24, 9]]),
columns=['name', 'attr11', 'attr12']
)
df2 = pd.DataFrame(np.array([
['a', 5, 19],
['b', 14, 16],
['c', 4, 9]]),
columns=['name', 'attr21', 'attr22']
)
df3 = pd.DataFrame(np.array([
['a', 15, 49],
['b', 4, 36],
['c', 14, 9]]),
columns=['name', 'attr31', 'attr32']
)

简单的解决方案:

如果列名相似:

 df1.merge(df2,on='col_name').merge(df3,on='col_name')

如果列名不同:

df1.merge(df2,left_on='col_name1', right_on='col_name2').merge(df3,left_on='col_name1', right_on='col_name3').drop(columns=['col_name2', 'col_name3']).rename(columns={'col_name1':'col_name'})

我调整了接受的答案,使用reduce对不同的suffix参数上的多个数据帧执行操作,我猜它也可以扩展到不同的on参数。

from functools import reduce


dfs_with_suffixes = [(df2,suffix2), (df3,suffix3),
(df4,suffix4)]


merge_one = lambda x,y,sfx:pd.merge(x,y,on=['col1','col2'..], suffixes=sfx)


merged = reduce(lambda left,right:merge_one(left,*right), dfs_with_suffixes, df1)
df1 = pd.DataFrame(np.array([
['a', 5, 9],
['b', 4, 61],
['c', 24, 9]]),
columns=['name', 'attr11', 'attr12']
)
df2 = pd.DataFrame(np.array([
['a', 5, 19],
['d', 14, 16]]


),
columns=['name', 'attr21', 'attr22']
)
df3 = pd.DataFrame(np.array([
['a', 15, 49],
['c', 4, 36],
['d', 14, 9]]),
columns=['name', 'attr31', 'attr32']
)
df4 = pd.DataFrame(np.array([
['a', 15, 49],
['c', 4, 36],
['c', 14, 9]]),
columns=['name', 'attr41', 'attr42']
)


加入列表数据框架的三种方法

  1. pandas.concat
dfs = [df1, df2, df3]
dfs = [df.set_index('name') for df in dfs]
# cant not run if index not unique
dfs = pd.concat(dfs, join='outer', axis = 1)
  1. functools.reduce
dfs = [df1, df2, df3, df4]
# still run with index not unique
import functools as ft
df_final = ft.reduce(lambda left, right: pd.merge(left, right, on='name', how = 'outer'), dfs)
  1. join
# cant not run if index not unique
dfs = [df1, df2, df3]
dfs = [df.set_index('name') for df in dfs]
dfs[0].join(dfs[1:], how = 'outer')