如何合并多个数据框架

我有不同的数据框架,需要根据日期列将它们合并在一起。如果我只有两个数据帧,我可以使用 df1.merge(df2, on='date'),使用三个数据帧,我使用 df1.merge(df2.merge(df3, on='date'), on='date'),但是它变得非常复杂,无法读取多个数据帧。

所有数据框架都有一个共同的列 -date,但是它们没有相同的行数和列数,我只需要那些每个日期对每个数据框架都是共同的行。

因此,我尝试编写一个递归函数,它返回一个包含所有数据的数据框架,但是它没有工作。那么我应该如何合并多个数据框架呢?

我尝试了不同的方法,得到了像 out of rangekeyerror 0/1/2/3can not merge DataFrame with instance of type <class 'NoneType'>这样的错误。

这是我写的剧本:

dfs = [df1, df2, df3] # list of dataframes


def mergefiles(dfs, countfiles, i=0):
if i == (countfiles - 2): # it gets to the second to last and merges it with the last
return
    

dfm = dfs[i].merge(mergefiles(dfs[i+1], countfiles, i=i+1), on='date')
return dfm


print(mergefiles(dfs, len(dfs)))

举个例子: Df _ 1:

May 19, 2017;1,200.00;0.1%
May 18, 2017;1,100.00;0.1%
May 17, 2017;1,000.00;0.1%
May 15, 2017;1,901.00;0.1%

Df _ 2:

May 20, 2017;2,200.00;1000000;0.2%
May 18, 2017;2,100.00;1590000;0.2%
May 16, 2017;2,000.00;1230000;0.2%
May 15, 2017;2,902.00;1000000;0.2%

Df _ 3:

May 21, 2017;3,200.00;2000000;0.3%
May 17, 2017;3,100.00;2590000;0.3%
May 16, 2017;3,000.00;2230000;0.3%
May 15, 2017;3,903.00;2000000;0.3%

预期合并结果:

May 15, 2017;  1,901.00;0.1%;  2,902.00;1000000;0.2%;   3,903.00;2000000;0.3%
371892 次浏览

有两种解决方案,但它分别返回所有列:

import functools


dfs = [df1, df2, df3]


df_final = functools.reduce(lambda left,right: pd.merge(left,right,on='date'), dfs)
print (df_final)
date     a_x   b_x       a_y      b_y   c_x         a        b   c_y
0  May 15,2017  900.00  0.2%  1,900.00  1000000  0.2%  2,900.00  2000000  0.2%


k = np.arange(len(dfs)).astype(str)
df = pd.concat([x.set_index('date') for x in dfs], axis=1, join='inner', keys=k)
df.columns = df.columns.map('_'.join)
print (df)
0_a   0_b       1_a      1_b   1_c       2_a      2_b   2_c
date
May 15,2017  900.00  0.2%  1,900.00  1000000  0.2%  2,900.00  2000000  0.2%

如果按照公共日期进行过滤,这将返回:

dfs = [df1, df2, df3]
checker = dfs[-1]
check = set(checker.loc[:, 0])


for df in dfs[:-1]:
check = check.intersection(set(df.loc[:, 0]))


print(checker[checker.loc[:, 0].isin(check)])

如果不涉及复杂的查询,下面是合并多个数据框架的最简洁、最易理解的方法。

只需将 日期作为索引进行合并,然后使用 外面方法进行合并(以获取所有数据)。

import pandas as pd
from functools import reduce


df1 = pd.read_table('file1.csv', sep=',')
df2 = pd.read_table('file2.csv', sep=',')
df3 = pd.read_table('file3.csv', sep=',')

现在,基本上把所有的文件作为数据框加载到一个列表中。然后,使用 mergereduce函数合并文件。

# compile the list of dataframes you want to merge
data_frames = [df1, df2, df3]

注意: 您可以在上面的列表中添加尽可能多的数据帧。这就是这个方法的好处。不涉及复杂的查询。

要保持属于同一日期的值,需要将其合并到 DATE

df_merged = reduce(lambda  left,right: pd.merge(left,right,on=['DATE'],
how='outer'), data_frames)


# if you want to fill the values that don't exist in the lines of merged dataframe simply fill with required strings as


df_merged = reduce(lambda  left,right: pd.merge(left,right,on=['DATE'],
how='outer'), data_frames).fillna('void')
  • 现在,输出将同一日期的值放在同一行上。
  • 可以使用 fill na ()从不同的框架为不同的列填充不存在的数据。

然后,如果需要,将合并的数据写入 csv 文件。

pd.DataFrame.to_csv(df_merged, 'merged.txt', sep=',', na_rep='.', index=False)

这个应该能给你

DATE VALUE1 VALUE2 VALUE3 ....

看起来数据有相同的列,所以你可以:

df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)


merged_df = pd.concat([df1, df2])

感谢您的帮助 @ Jezrael@ zipa@ everestial007,这两个答案是我所需要的。如果我想做一个递归,这也可以按预期的方式工作:

def mergefiles(dfs=[], on=''):
"""Merge a list of files based on one column"""
if len(dfs) == 1:
return "List only have one element."


elif len(dfs) == 2:
df1 = dfs[0]
df2 = dfs[1]
df = df1.merge(df2, on=on)
return df


# Merge the first and second datafranes into new dataframe
df1 = dfs[0]
df2 = dfs[1]
df = dfs[0].merge(dfs[1], on=on)


# Create new list with merged dataframe
dfl = []
dfl.append(df)


# Join lists
dfl = dfl + dfs[2:]
dfm = mergefiles(dfl, on)
return dfm

@ dannyeuu 的回答是正确的。Concat 自然会对索引列进行连接,如果将 Axis 选项设置为1的话。默认情况下是外部连接,但也可以指定内部连接。这里有一个例子:

x = pd.DataFrame({'a': [2,4,3,4,5,2,3,4,2,5], 'b':[2,3,4,1,6,6,5,2,4,2], 'val': [1,4,4,3,6,4,3,6,5,7], 'val2': [2,4,1,6,4,2,8,6,3,9]})
x.set_index(['a','b'], inplace=True)
x.sort_index(inplace=True)


y = x.__deepcopy__()
y.loc[(14,14),:] = [3,1]
y['other']=range(0,11)


y.sort_values('val', inplace=True)


z = x.__deepcopy__()
z.loc[(15,15),:] = [3,4]
z['another']=range(0,22,2)
z.sort_values('val2',inplace=True)




pd.concat([x,y,z],axis=1)

Reduce < strong > pd.concat 是很好的解决方案,但就执行时间而言,pd.concat 是最好的。

from functools import reduce
import pandas as pd


dfs = [df1, df2, df3, ...]
nan_value = 0


# solution 1 (fast)
result_1 = pd.concat(dfs, join='outer', axis=1).fillna(nan_value)


# solution 2
result_2 = reduce(lambda df_left,df_right: pd.merge(df_left, df_right,
left_index=True, right_index=True,
how='outer'),
dfs).fillna(nan_value)

看看这个 熊猫三向连接多个数据框架的列

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

@ everestal007的解决方案对我很有效。这就是我为我的用例改进它的方法,它使每个不同 df 的列具有不同的后缀,这样我就可以更容易地区分最终合并数据框架中的 dfs。

from functools import reduce
import pandas as pd
dfs = [df1, df2, df3, df4]
suffixes = [f"_{i}" for i in range(len(dfs))]
# add suffixes to each df
dfs = [dfs[i].add_suffix(suffixes[i]) for i in range(len(dfs))]
# remove suffix from the merging column
dfs = [dfs[i].rename(columns={f"date{suffixes[i]}":"date"}) for i in range(len(dfs))]
# merge
dfs = reduce(lambda left,right: pd.merge(left,right,how='outer', on='date'), dfs)

另一种组合方式: functools.reduce

来自文档:

例如,reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])计算(((1 + 2) + 3) + 4) + 5)。左边的参数 x 是累计值,右边的参数 y 是迭代器的更新值。

所以:

from functools import reduce
dfs = [df1, df2, df3, df4, df5, df6]
df_final = reduce(lambda left,right: pd.merge(left,right,on='some_common_column_name'), dfs)

对我来说,索引没有显式指令就被忽略了。例如:

    > x = pandas.DataFrame({'a': [1,2,2], 'b':[4,5,5]})
> x
a   b
0   1   4
1   2   5
2   2   5


> x.drop_duplicates()
a   b
0   1   4
1   2   5

(尽管不同的索引删除重复行)

我有一个类似的用例,并在下面解决了。基本上捕获列表中的第一个 df,然后循环通过提醒并合并它们,合并的结果将取代前一个 df。

编辑: 我正在处理一些相当小的数据框架——不确定这种方法将如何扩展到更大的数据集。 # caveatemptor

import pandas as pd
df_list = [df1,df2,df3, ...dfn]
# grab first dataframe
all_merged = df_list[0]
# loop through all but first data frame
for to_merge in df_list[1:]:
# result of merge replaces first or previously
# merged data frame w/ all previous fields
all_merged = pd.merge(
left=all_merged
,right=to_merge
,how='inner'
,on=['some_fld_across_all']
)


# can easily have this logic live in a function
def merge_mult_dfs(df_list):
all_merged = df_list[0]
for to_merge in df_list[1:]:
all_merged = pd.merge(
left=all_merged
,right=to_merge
,how='inner'
,on=['some_fld_across_all']
)
return all_merged