将多个 csv 文件连接成具有相同标题的单个 csv - Python

Posted

技术标签:

【中文标题】将多个 csv 文件连接成具有相同标题的单个 csv - Python【英文标题】:Concatenating multiple csv files into a single csv with the same header - Python 【发布时间】:2017-12-01 03:39:49 【问题描述】:

我目前正在使用以下代码导入 6,000 个 csv 文件(带有标题)并将它们导出到单个 csv 文件(带有单个标题行)。

#import csv files from folder
path =r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
stockstats_data = pd.DataFrame()
list_ = []

for file_ in allFiles:
    df = pd.read_csv(file_,index_col=None,)
    list_.append(df)
    stockstats_data = pd.concat(list_)
    print(file_ + " has been imported.")

这段代码运行良好,但速度很慢。最多可能需要 2 天的时间来处理。

我得到了一个终端命令行的单行脚本,它执行相同的操作(但没有标题)。此脚本需要 20 秒。

 for f in *.csv; do cat "`pwd`/$f" | tail -n +2 >> merged.csv; done 

有人知道如何加快第一个 Python 脚本的速度吗?为了缩短时间,我考虑过不将其导入 DataFrame 而只是连接 CSV,但我无法弄清楚。

谢谢。

【问题讨论】:

【参考方案1】:

如果您不需要内存中的 CSV,只需从输入复制到输出,那么完全避免解析会便宜很多,并且复制时不会在内存中建立:

import shutil
import glob


#import csv files from folder
path = r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
allFiles.sort()  # glob lacks reliable ordering, so impose your own if output order matters
with open('someoutputfile.csv', 'wb') as outfile:
    for i, fname in enumerate(allFiles):
        with open(fname, 'rb') as infile:
            if i != 0:
                infile.readline()  # Throw away header on all but first file
            # Block copy rest of file from input to output without parsing
            shutil.copyfileobj(infile, outfile)
            print(fname + " has been imported.")

就是这样; shutil.copyfileobj 有效地处理数据复制,大大减少了 Python 级别的解析和重新序列化工作。

这假设所有 CSV 文件都具有相同的格式、编码、行尾等,并且标题不包含嵌入的换行符,但如果是这种情况,它会比替代方案快得多。

【讨论】:

@ShadowRanger,您能否分享一些将大 csv 拆分为多个文件并在每个小文件中保留标题的方法? @vikrantrana:这是一个完全不同的问题,不太适合在 cmets 中回答,也不适合回答 OP 的问题。假设many questions on this topic 之一没有涵盖它,请随时就该主题提出您自己的问题。不过,需要更多详细信息才能回答(例如,您是否按行数、字节数等进行拆分),并且csv 模块将是必要的(因为您需要它来正确分隔行)。 @vikrantrana:警告:在一般情况下,您无法像在这种情况下那样优化建议的模式; csv 解析将是必要的(因为虽然通常可以将标头视为不包含嵌入的换行符,但不能假定任意 CSV 数据具有这种行为),并且解析比仅像这样的原始字节副本要昂贵得多问题允许;您可以将其中的很多内容推送到 C 层(csv.readercsv.writer 在 C 中实现),但必须完成(相对)昂贵的解析工作。 跟进,因为这很重要:您不能依赖 glob 模块以特定顺序返回文件,如所写,这段代码(和所有其他 Python+glob 模块答案)将无法可靠地从包含 a.csvb.csv 的目录中按字母(或任何其他有用的)顺序读取;它会因操作系统、文件系统以及相关目录中文件创建/删除的整个历史而异。因此,假设生成的 CSV 的内容应该以某种可靠的顺序出现,您将需要明确地对 glob.glob 调用的结果进行排序。 我已更新答案以对结果进行简单(“自然”)排序以获得可重现的行为。有关此 glob 模块行为何时导致问题的更多信息,请参阅 A Code Glitch May Have Caused Errors In More Than 100 Published Studies。【参考方案2】:

这是一种更简单的方法 - 您可以使用 pandas(虽然我不确定它对 RAM 的使用有什么帮助)-

import pandas as pd
import glob

path =r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
stockstats_data = pd.DataFrame()
list_ = []

for file_ in allFiles:
    df = pd.read_csv(file_)
    stockstats_data = pd.concat((df, stockstats_data), axis=0)

【讨论】:

【参考方案3】:

您不需要 pandas,只需简单的 csv 模块即可。

import csv

df_out_filename = 'df_out.csv'
write_headers = True
with open(df_out_filename, 'wb') as fout:
    writer = csv.writer(fout)
    for filename in allFiles:
        with open(filename) as fin:
            reader = csv.reader(fin)
            headers = reader.next()
            if write_headers:
                write_headers = False  # Only write headers once.
                writer.writerow(headers)
            writer.writerows(reader)  # Write all remaining rows.

【讨论】:

这有几个问题:1)您以文本模式打开输入文件,以二进制格式输出,这在 Py3 上根本不起作用,即使在 Py2 上也是错误的 Windows 框(您可以将输入中的\r\n 行尾转换为输出中的\n 行尾)。遗憾的是,如果不付出大量努力或第三方模块,就不可能使其完全可移植(因为csv 模块在 Py2 上需要二进制 I/O,在 Py3 上需要带有 newline='' 的文本 I/O)。 2)(次要)如果不出意外,headers = reader.next() 可以更改为headers = next(reader) 以使其适用于 2.6-3.x,而不仅仅是 2.x。【参考方案4】:

您是否需要在 Python 中执行此操作?如果您愿意完全在 shell 中执行此操作,您需要做的就是首先将随机选择的输入 .csv 文件中的标题行 cat 放入 merged.csv,然后再运行您的单行:

cat a-randomly-selected-csv-file.csv | head -n1 > merged.csv
for f in *.csv; do cat "`pwd`/$f" | tail -n +2 >> merged.csv; done 

【讨论】:

感谢您的帮助 - 我在 Python 中确实需要它作为更大项目的一部分。干杯。 @mattblack,我本该想到的。希望亚历山大的回答对你有用!

以上是关于将多个 csv 文件连接成具有相同标题的单个 csv - Python的主要内容,如果未能解决你的问题,请参考以下文章

合并具有不同列名但定义相同的多个CSV

将具有不同架构(列)的多个文件 (.csv) 合并/合并为单个文件 .csv - Azure 数据工厂

将文件夹中的多个csv文件读入R中的单个数据框[重复]

是否可以以相同或不同的顺序将具有相同标题或标题子集的多个 csv 文件读取到 spark 数据帧中?

形成一个循环将多个 .csv 文件连接成一个 .csv 文件

如何将mysql多个outfiles转换成单个压缩zip