【问题标题】:Python: pandas merge multiple dataframesPython:熊猫合并多个数据框
【发布时间】:2017-11-03 19:26:43
【问题描述】:

我有不同的数据框,需要根据日期列将它们合并在一起。如果我只有两个数据帧,我可以使用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%   

【问题讨论】:

标签: python pandas dataframe merge data-analysis


【解决方案1】:

对我来说,索引在没有明确说明的情况下被忽略。示例:

    > 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

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

【讨论】:

    【解决方案2】:

    另一种组合方式: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)
    

    【讨论】:

      【解决方案3】:

      @everestial007 的解决方案对我有用。这就是我为我的用例改进它的方式,即让每个不同 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)
      

      【讨论】:

        【解决方案4】:

        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)
        

        【讨论】:

        • 你使用on=...吗?
        【解决方案5】:

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

        只需简单地将 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 吗?
        • 请注意:如果您使用的是 python3,则需要从 functools 导入 reduce
        • 除了@NicolasMartinez 提到的:from functools import reduce # only in Python 3
        【解决方案6】:

        看看这个pandas three-way joining multiple dataframes on columns

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

        【讨论】:

          【解决方案7】:

          感谢您的帮助 @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
          

          【讨论】:

            【解决方案8】:

            @dannyeuu 的回答是正确的。如果您将轴选项设置为 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)
            

            【讨论】:

              【解决方案9】:

              看起来数据具有相同的列,因此您可以:

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

              【讨论】:

              • 如果你没有相同的列怎么办?
              • 不错。如果有相同的列要合并,我们可以使用它。
              • concat 可以通过索引自动加入,所以如果你有相同的列,请将它们设置为索引@Gerard
              【解决方案10】:

              如果您按共同日期过滤,这将返回它:

              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])))吗?
              • @VascoFerreira 我也编辑了代码以匹配这种情况。
              【解决方案11】:

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

              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%
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-02-10
                • 2020-08-16
                • 2021-05-30
                • 1970-01-01
                相关资源
                最近更新 更多