Python:熊猫合并了多个数据框


81

我有不同的数据框,需要根据日期列将它们合并在一起。如果我只有两个数据框,则可以使用df1.merge(df2, on='date')来对三个数据框进行处理df1.merge(df2.merge(df3, on='date'), on='date'),但是,使用多个数据框进行处理将变得非常复杂且难以理解。

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

因此,我正在尝试编写一个递归函数,该函数返回一个包含所有数据的数据框,但是它不起作用。那我应该如何合并多个数据框?

我试图diferent方式和喜欢了错误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%   

Answers:


129

下面是在不涉及复杂查询的情况下合并多个数据框的最简洁,可理解的方法。

只需将DATE作为索引合并,然后使用OUTER方法合并(以获取所有数据)。

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')
  • 现在,输出将显示同一行中同一日期的值。
  • 您可以使用fillna()填充来自不同框架的不同列的不存在的数据。

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

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

这应该给你

DATE VALUE1 VALUE2 VALUE3 ....


如果连接列不同怎么办?如果连接列不同,我们应该使用pd.merge吗?
史蒂夫

4
一点点注意:如果您使用的是python3,则需要从functools中导入reduce
Nicolas Martinez

除了@NicolasMartinez提到的内容外:from functools import reduce # only in Python 3
jengeb


17

functools.reducepd.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)

9

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

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%

4

@dannyeuu的答案是正确的。如果将axis选项设置为1,则pd.concat自然会在索引列上执行联接。默认值为外部联接,但也可以指定内部联接。这是一个例子:

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)


0

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

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)])

但这样只能得到3个文件的结果。如果我尝试使用4个文件怎么办?我需要做的:set(df1.loc[:, 0].intersection(set(df3.loc[:, 0]).intersection(set(df2.loc[:, 0])).intersection(set(df1.loc[:, 0])))
Vasco Ferreira

@VascoFerreira我也编辑了代码以适应这种情况。
zipa

0

谢谢你的帮助@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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.