【问题标题】:Import multiple csv files into pandas and concatenate into one DataFrame将多个csv文件导入pandas并拼接成一个DataFrame
【发布时间】:2014-01-21 06:57:05
【问题描述】:

我想将一个目录中的几个 csv 文件读入 pandas 并将它们连接到一个大 DataFrame 中。我一直无法弄清楚。这是我目前所拥有的:

import glob
import pandas as pd

# get data file names
path =r'C:\DRO\DCL_rawdata_files'
filenames = glob.glob(path + "/*.csv")

dfs = []
for filename in filenames:
    dfs.append(pd.read_csv(filename))

# Concatenate all data into one DataFrame
big_frame = pd.concat(dfs, ignore_index=True)

我想我在 for 循环中需要一些帮助???

【问题讨论】:

  • 您的代码什么都不做,因为您没有附加到您的dfs 列表,您不想用dfs.append(pd.read_csv(filename) 替换行data = pd.read_csv(filename)。然后,您需要遍历列表和concat,我认为concat 不会在dfs 的列表上工作。
  • 您还在最后一行中将模块的别名与模块名称混合在一起,不应该是big_frame = pd.concat(dfs, ignore_index=True)吗?无论如何,一旦您拥有数据框列表,您将需要迭代列表并连接到big_frame
  • 是的,我编辑了代码,但我仍然无法从 csv 文件构建串联数据框,我是 python 新手,所以我需要更多帮助
  • 你现在需要遍历dfs,所以for df in dfs: big_frame.concat(df, ignore_index=True) 应该可以工作,你也可以尝试append 而不是concat
  • 您能更准确地说明什么不起作用吗?因为concat 应该像您一样处理数据帧列表。我认为这是一个非常好的方法。

标签: python pandas csv dataframe concatenation


【解决方案1】:

如果您在所有csv 文件中都有相同的列,那么您可以尝试下面的代码。 我添加了header=0,以便在阅读csv 后可以将第一行指定为列名。

import pandas as pd
import glob

path = r'C:\DRO\DCL_rawdata_files' # use your path
all_files = glob.glob(path + "/*.csv")

li = []

for filename in all_files:
    df = pd.read_csv(filename, index_col=None, header=0)
    li.append(df)

frame = pd.concat(li, axis=0, ignore_index=True)

【讨论】:

  • 这似乎是一种老式的手工做事方式,尤其是。由于 Hapood 生态系统拥有越来越多的工具,您可以在其中直接对包含不同文件类型(csv、json、txt、数据库)的许多不同目录执行 sql 查询,就好像它是一个数据源一样。在 python 中一定有类似的东西,因为它在做“大数据”方面已经有了 20 年的飞跃开始。
  • 同样的事情更简洁,也许更快,因为它不使用列表:df = pd.concat((pd.read_csv(f) for f in all_files)) 此外,也许应该使用os.path.join(path, "*.csv") 而不是path + "/*.csv",这使得它独立于操作系统。
  • 使用这个答案允许我在 for file_ 循环中添加带有文件名的新列,例如 df['filename'] = os.path.basename(file_) ..不确定 Sid 的答案是否允许这样做?
  • @curtisp 您仍然可以使用 Sid 的回答来做到这一点,只需在生成器中使用 pandas.read_csv(f).assign(filename = foo)assign 将返回整个数据框,包括新列 filename
  • 这是第一个明确的答案,我能够找到描述将多个 csv 组合到列表中的帽子,然后将组合转换为数据框,而无需先定义数据框列。我通过将filename 替换为`io.StringIO(response.content.decode('utf-8'))`,为我的用例修改了这个答案,结合了多个requests.get(url) csv 响应
【解决方案2】:

darindaCoder's answer 的替代方案:

path = r'C:\DRO\DCL_rawdata_files'                     # use your path
all_files = glob.glob(os.path.join(path, "*.csv"))     # advisable to use os.path.join as this makes concatenation OS independent

df_from_each_file = (pd.read_csv(f) for f in all_files)
concatenated_df   = pd.concat(df_from_each_file, ignore_index=True)
# doesn't create a list, nor does it append to one

【讨论】:

  • @Mike @Sid 最后两行可以替换为:pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)。 Pandas 0.18.1 版需要内括号
  • 我建议使用glob.iglob 而不是glob.glob;第一个返回和iterator (instead of a list)
【解决方案3】:
import glob
import os
import pandas as pd   
df = pd.concat(map(pd.read_csv, glob.glob(os.path.join('', "my_files*.csv"))))

【讨论】:

  • 优秀的一个班轮,如果不需要 read_csv 参数特别有用!
  • 另一方面,如果需要参数,可以使用 lambdas 来完成:df = pd.concat(map(lambda file: pd.read_csv(file, delim_whitespace=True), data_files))
  • ^ 或 functools.partial,以避免 lambdas
【解决方案4】:

这里的几乎所有答案要么过于复杂(glob 模式匹配),要么依赖于额外的 3rd 方库。您可以使用 Pandas 和 python(所有版本)已经内置的所有内容在 2 行中完成此操作。

对于一些文件 - 1 班轮

df = pd.concat(map(pd.read_csv, ['d1.csv', 'd2.csv','d3.csv']))

对于许多文件

import os

filepaths = [f for f in os.listdir(".") if f.endswith('.csv')]
df = pd.concat(map(pd.read_csv, filepaths))

无标题

如果您想用 pd.read_csv 更改特定的内容(即没有标题),您可以创建一个单独的函数并使用您的地图调用它:

def f(i):
    return pd.read_csv(i, header=None)

df = pd.concat(map(f, filepaths))

这条设置 df 的 pandas 行利用了 3 个东西:

  1. Python's map (function, iterable) 发送到函数( pd.read_csv()) 每个 csv 元素的可迭代(我们的列表) 在文件路径中)。
  2. Panda 的 read_csv() 函数正常读取每个 CSV 文件。
  3. Panda 的 concat() 将所有这些都集中在一个 df 变量中。

【讨论】:

  • 或只是df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv))
  • 我尝试了@muon规定的方法。但是,我有多个带有标题的文件(标题很常见)。我不希望它们在数据框中连接起来。你知道我该怎么做吗?我试过df = pd.concat(map(pd.read_csv(header=0), glob.glob('data/*.csv)) 但它给出了一个错误“parser_f() missing 1 required positional argument: 'filepath_or_buffer'”
  • 您已经有一段时间没有问过了...但我更新了我的答案以包含没有标题的答案(或者如果您想将任何更改传递给 read_csv)。
【解决方案5】:

简单快捷

导入两个或多个csv,而无需列出名称。

import glob
import pandas as pd

df = pd.concat(map(pd.read_csv, glob.glob('data/*.csv')))

【讨论】:

【解决方案6】:

Dask 库可以从多个文件中读取数据帧:

>>> import dask.dataframe as dd
>>> df = dd.read_csv('data*.csv')

(来源:https://examples.dask.org/dataframes/01-data-access.html#Read-CSV-files

Dask 数据帧实现了 Pandas 数据帧 API 的一个子集。如果所有数据都适合内存,您可以call df.compute() 将数据帧转换为 Pandas 数据帧。

【讨论】:

  • 与此类似,pandas API 中应该有一个函数可以读取一个目录中的多个文件。显然它没有它,就像现在一样。
【解决方案7】:

编辑:我用谷歌搜索了https://stackoverflow.com/a/21232849/186078。 但是最近我发现使用 numpy 进行任何操作然后将其分配给数据框一次而不是在迭代的基础上操作数据框本身更快,并且它似乎也适用于此解决方案。

我真诚地希望任何访问此页面的人考虑这种方法,但不想将这么大的代码作为评论附加,使其可读性降低。

您可以利用 numpy 真正加快数据帧连接的速度。

import os
import glob
import pandas as pd
import numpy as np

path = "my_dir_full_path"
allFiles = glob.glob(os.path.join(path,"*.csv"))


np_array_list = []
for file_ in allFiles:
    df = pd.read_csv(file_,index_col=None, header=0)
    np_array_list.append(df.as_matrix())

comb_np_array = np.vstack(np_array_list)
big_frame = pd.DataFrame(comb_np_array)

big_frame.columns = ["col1","col2"....]

时间统计:

total files :192
avg lines per file :8492
--approach 1 without numpy -- 8.248656988143921 seconds ---
total records old :1630571
--approach 2 with numpy -- 2.289292573928833 seconds ---

【讨论】:

  • 有什么数字可以支持“加速”吗?具体来说,是不是比stackoverflow.com/questions/20906474/…快?
  • 我没有看到 OP 要求一种方法来加快他的连接速度,这看起来像是对预先存在的已接受答案的返工。
  • 如果数据有混合列类型,这将不起作用。
  • @SKG 完美.. 这对我来说是唯一可行的解​​决方案。 2 秒内总共 500 个文件 400k 行。感谢您发布它。
  • 1500 个文件和 750k 行在 5 秒内。优秀的@SKG
【解决方案8】:

一个使用map的衬里,但如果您想指定其他参数,您可以这样做:

import pandas as pd
import glob
import functools

df = pd.concat(map(functools.partial(pd.read_csv, sep='|', compression=None), 
                    glob.glob("data/*.csv")))

注意:map 本身不允许您提供额外的参数。

【讨论】:

    【解决方案9】:

    如果您想递归搜索Python 3.5 或更高版本),您可以执行以下操作:

    from glob import iglob
    import pandas as pd
    
    path = r'C:\user\your\path\**\*.csv'
    
    all_rec = iglob(path, recursive=True)     
    dataframes = (pd.read_csv(f) for f in all_rec)
    big_dataframe = pd.concat(dataframes, ignore_index=True)
    

    注意最后三行可以用一个单行表示:

    df = pd.concat((pd.read_csv(f) for f in iglob(path, recursive=True)), ignore_index=True)
    

    你可以找到**here的文档。另外,我使用iglob而不是glob,因为它返回一个迭代器而不是一个列表。



    编辑:多平台递归函数:

    您可以将以上内容包装成一个多平台函数(Linux、Windows、Mac),这样您就可以:

    df = read_df_rec('C:\user\your\path', *.csv)
    

    函数如下:

    from glob import iglob
    from os.path import join
    import pandas as pd
    
    def read_df_rec(path, fn_regex=r'*.csv'):
        return pd.concat((pd.read_csv(f) for f in iglob(
            join(path, '**', fn_regex), recursive=True)), ignore_index=True)
    

    【讨论】:

      【解决方案10】:

      如果多个csv文件被压缩,你可以使用zipfile读取所有并连接如下:

      import zipfile
      import pandas as pd
      
      ziptrain = zipfile.ZipFile('yourpath/yourfile.zip')
      
      train = []
      
      train = [ pd.read_csv(ziptrain.open(f)) for f in ziptrain.namelist() ]
      
      df = pd.concat(train)
      
          
      

      【讨论】:

        【解决方案11】:

        另一个具有列表理解的在线工具,允许使用 read_csv 的参数。

        df = pd.concat([pd.read_csv(f'dir/{f}') for f in os.listdir('dir') if f.endswith('.csv')])
        

        【讨论】:

          【解决方案12】:

          使用pathlib 库的替代方法(通常优于os.path)。

          这种方法避免了熊猫concat()/apped()的迭代使用。

          来自 pandas 文档:
          值得注意的是,concat()(因此 append())会生成数据的完整副本,并且不断重用此函数会显着降低性能。如果您需要对多个数据集使用该操作,请使用列表推导式。

          import pandas as pd
          from pathlib import Path
          
          dir = Path("../relevant_directory")
          
          df = (pd.read_csv(f) for f in dir.glob("*.csv"))
          df = pd.concat(df)
          

          【讨论】:

            【解决方案13】:

            基于@Sid 的好答案。

            在连接之前,您可以将 csv 文件加载到一个中间字典中,该字典允许根据文件名访问每个数据集(格式为 dict_of_df['filename.csv'])。例如,当列名未对齐时,这样的字典可以帮助您识别异构数据格式的问题。

            导入模块并定位文件路径:

            import os
            import glob
            import pandas
            from collections import OrderedDict
            path =r'C:\DRO\DCL_rawdata_files'
            filenames = glob.glob(path + "/*.csv")
            

            注意:OrderedDict 不是必需的, 但它会保留可能对分析有用的文件顺序。

            将 csv 文件加载到字典中。然后连接:

            dict_of_df = OrderedDict((f, pandas.read_csv(f)) for f in filenames)
            pandas.concat(dict_of_df, sort=True)
            

            键是文件名f,值是csv文件的数据框内容。 除了使用f 作为字典键之外,您还可以使用os.path.basename(f) 或其他os.path 方法将字典中的键的大小减小到仅相关的较小部分。

            【讨论】:

              【解决方案14】:
              import os
              
              os.system("awk '(NR == 1) || (FNR > 1)' file*.csv > merged.csv")
              

              其中NRFNR 表示正在处理的行号。

              FNR 是每个文件中的当前行。

              NR == 1 包括第一个文件的第一行(标题),而FNR > 1 跳过每个后续文件的第一行。

              【讨论】:

                【解决方案15】:

                如果有人遇到未命名列的问题,可以使用此代码沿 x 轴合并多个 csv 文件。

                import glob
                import os
                import pandas as pd
                
                merged_df = pd.concat([pd.read_csv(csv_file, index_col=0, header=0) for csv_file in glob.glob(
                        os.path.join("data/", "*.csv"))], axis=0, ignore_index=True)
                    
                merged_df.to_csv("merged.csv")
                

                【讨论】:

                  【解决方案16】:

                  灵感来自MrFunanswer

                  import glob
                  import pandas as pd
                  
                  list_of_csv_files = glob.glob(directory_path + '/*.csv')
                  list_of_csv_files.sort()
                  
                  df = pd.concat(map(pd.read_csv, list_of_csv_files), ignore_index=True)
                  

                  注意事项:

                  1. 默认情况下,通过glob.glob生成的文件列表是不排序的。另一方面,在许多情况下,它需要进行排序,例如可能需要分析传感器帧丢弃数与时间戳的关系。

                  2. pd.concat 命令中,如果未指定ignore_index=True,则它会保留每个数据帧(即列表中的每个单独的CSV 文件)的原始索引,并且主数据帧看起来像

                        timestamp    id    valid_frame
                    0
                    1
                    2
                    .
                    .
                    .
                    0
                    1
                    2
                    .
                    .
                    .
                    

                    使用ignore_index=True,它看起来像:

                        timestamp    id    valid_frame
                    0
                    1
                    2
                    .
                    .
                    .
                    108
                    109
                    .
                    .
                    .
                    

                    IMO,当人们可能想要手动创建帧丢弃数与一分钟(或任何其他持续时间)箱的直方图并希望基于第一个时间戳进行计算时,这很有帮助,例如 begin_timestamp = df['timestamp'][0]

                    如果没有,ignore_index=Truedf['timestamp'][0] 从所有单独的数据帧中生成包含第一个时间戳的系列,它不只给出一个值。

                  【讨论】:

                    【解决方案17】:

                    你也可以这样做:

                    import pandas as pd
                    import os
                    
                    new_df = pd.DataFrame()
                    for r, d, f in os.walk(csv_folder_path):
                        for file in f:
                            complete_file_path = csv_folder_path+file
                            read_file = pd.read_csv(complete_file_path)
                            new_df = new_df.append(read_file, ignore_index=True)
                    
                    
                    new_df.shape
                    

                    【讨论】:

                      【解决方案18】:

                      考虑使用convtools 库,它提供了大量数据处理原语并在后台生成简单的临时代码。 它不应该比 pandas/polars 快,但有时可以。

                      例如您可以将 csv 文件合并为一个以供进一步重用 - 这是代码:

                      import glob
                      
                      from convtools import conversion as c
                      from convtools.contrib.tables import Table
                      import pandas as pd
                      
                      
                      def test_pandas():
                          df = pd.concat(
                              (
                                  pd.read_csv(filename, index_col=None, header=0)
                                  for filename in glob.glob("tmp/*.csv")
                              ),
                              axis=0,
                              ignore_index=True,
                          )
                          df.to_csv("out.csv", index=False)
                      # took 20.9 s
                      
                      
                      def test_convtools():
                          table = None
                          for filename in glob.glob("tmp/*.csv"):
                              table_ = Table.from_csv(filename, header=False)
                              if table is None:
                                  table = table_
                              else:
                                  table = table.chain(table_)
                      
                          table.into_csv("out_convtools.csv", include_header=False)
                      # took 15.8 s
                      

                      当然,如果你只是想获得一个数据帧而不写一个连接文件,它会相应地使用4.63 s10.9 spandas在这里更快,因为它不需要压缩列来编写它返回)。

                      【讨论】:

                        【解决方案19】:
                        import pandas as pd
                        import glob
                        
                        path = r'C:\DRO\DCL_rawdata_files' # use your path
                        file_path_list = glob.glob(path + "/*.csv")
                        
                        file_iter = iter(file_path_list)
                        
                        list_df_csv = []
                        list_df_csv.append(pd.read_csv(next(file_iter)))
                        
                        for file in file_iter:
                            lsit_df_csv.append(pd.read_csv(file, header=0))
                        df = pd.concat(lsit_df_csv, ignore_index=True)
                        

                        【讨论】:

                          【解决方案20】:

                          这是在 Google Drive 上使用 Colab 的方法

                          import pandas as pd
                          import glob
                          
                          path = r'/content/drive/My Drive/data/actual/comments_only' # use your path
                          all_files = glob.glob(path + "/*.csv")
                          
                          li = []
                          
                          for filename in all_files:
                              df = pd.read_csv(filename, index_col=None, header=0)
                              li.append(df)
                          
                          frame = pd.concat(li, axis=0, ignore_index=True,sort=True)
                          frame.to_csv('/content/drive/onefile.csv')
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2014-01-21
                            • 2020-02-05
                            • 2019-07-15
                            • 2021-04-03
                            • 2023-02-01
                            相关资源
                            最近更新 更多