在Python中组合异构CSV文件的最快和I/O高效方法

woo*_*oot 2 python csv io optimization

给定10个1MB csv文件,每个文件的布局略有不同,我需要将它们组合成具有相同标题的规范化单个文件.空字符串适用于空值.

列的示例:

1. FIELD1, FIELD2, FIELD3
2. FIELD2, FIELD1, FIELD3
3. FIELD1, FIELD3, FIELD4
4. FIELD3, FIELD4, FIELD5, FIELD6
5. FIELD2
Run Code Online (Sandbox Code Playgroud)

输出看起来像(虽然顺序不重要,我的代码将它们按顺序发现):

FIELD1, FIELD2, FIELD3, FIELD4, FIELD5, FIELD6
Run Code Online (Sandbox Code Playgroud)

所以基本上字段可以以任何顺序出现,字段可能丢失,或者之前看不到的新字段.所有必须包含在输出文件中.不需要连接,最后部件中的数据行数必须等于输出中的行数.

将所有10MB读入内存即可.以某种方式使用100MB来做它不会.如果需要,您可以一次打开所有文件.大量的文件,可用的内存,但它将针对NAS运行,因此它需要高效(不是太多的NAS操作).

我现在的方法是将每个文件读入列列表,在发现新列时构建新列列表,然后将其全部写入单个文件.我希望有人有一些更聪明的东西,因为我是这个过程的瓶颈,所以任何缓解都是有帮助的.

如果有人想尝试,我在这里有样品文件.我将发布当前代码作为可能的答案.寻找使用本地磁盘在我的服务器(大量内核,大量内存)上运行它的最快时间.

Mar*_*ers 6

使用csv.DictReader()csv.DictWriter()对象的两遍方法.通过一个收集所有文件中使用的标头集,然后根据标头传递两个然后复制数据.

收集标题就像访问fieldnamesreader对象上的属性就足够了:

import csv
import glob

files = []
readers = []
fields = set()

try:
    for filename in glob.glob('in*.csv'):
        try:
            fileobj = open(filename, 'rb')
        except IOError:
            print "Failed to open {}".format(filename)
            continue
        files.append(fileobj)  # for later closing

        reader = csv.DictReader(fileobj)
        fields.update(reader.fieldnames)  # reads the first row
        readers.append(reader)

    with open('result.csv', 'wb') as outf:
        writer = csv.DictWriter(outf, fieldnames=sorted(fields))
        writer.writeheader()
        for reader in readers:
            # copy across rows; missing fields will be left blank
            for row in reader:
                writer.writerow(row)
finally:
    # close out open file objects
    for fileobj in files:
        fileobj.close()
Run Code Online (Sandbox Code Playgroud)

每个阅读器都会生成一个包含所有字段子集的字典,但DictWriter会使用restval参数的值(默认为''省略时,就像我在这里所做的那样)来填充每个缺失键的值.

我在这里假设Python 2; 如果这是Python 3,您可以使用a ExitStack()来管理读者的打开文件; 省略b文件模式并newline=''为所有打开的调用添加一个参数,以便将新行处理留给CSV模块.

上面的代码只使用缓冲区来读写行; 行一次一次一行地从一个打开的阅读器移动到一个写入器.

不幸的是,我们无法使用,writer.writerows(reader)因为DictWriter.writerows()实现首先将所有内容转换reader为列表列表,然后再将其传递给基础csv.writer.writerows()方法,请参阅Python错误跟踪器中的问题23495.