我有大约50k个类似csv的文件,用空格分隔,每个文件有大约数千万行。第一列总是字符串没有任何空格,第二列总是正整数,并且没有丢失的数据。在这个问题中,我只对第一列感兴趣,所以请忽略第二列。以下是两个这样的csv文件的玩具示例。
example1.csv
f1 x
f2 x
f5 x
f7 x
...
example2.csv
f1 x
f2 x
f3 x
f4 x
f6 x
...
如您所见,两个文件中的功能集重叠但不相同。我想要做的是组合所有50k csv文件的数据,并将其转换为以下形式。
file_name f1 f2 f3 f4 f5 f6 f7 ....
example1.csv 1 1 0 0 1 0 1 ...
example2.csv 1 1 1 1 0 1 0 ...
...
所以基本上构建一个file_name
x feature_id
的矩阵,如果文件中存在feature_id
,那么它是1,否则为0。这里的解析相对简单,重点是可伸缩性,并且在未来的项目中行数可能高达数十亿。我可以使用最多一个或两个TB的内存和100个内核的机器。所以我认为内存约束不是一个问题,但我的天真实现如下所示适用于玩具示例,但对于真实的实例来说太慢了,并且当它在第一个文件中达到大约310000行时似乎挂起,我就是不知道为什么。 (你知道为什么吗?我的直觉说它可能与defaultdict有关,不确定它是如何实现的,使用起来可能很昂贵。)我希望解决方案能够相当快。解决方案最好是在Python中,但其他语言也很好。
import os
import gzip
from collections import defaultdict
import pandas as pd
# collect paths to all csv-like files
with open('file_list.txt') as inf:
inputs = [_.split() for _ in inf.readlines]
inputs_map = dict(zip(inputs, range(len(inputs))))
res = defaultdict(lambda :[0] * len(inputs))
for k, infile in enumerate(inputs):
print(k, infile)
source_file = os.path.abspath(infile)
source_file_id = inputs_map[source_file]
# starting parsing the csv-like file
with gzip.open(source_file, 'rt') as inf:
for kl, line in enumerate(inf):
feature_id = line.split()[0]
res[feature_id][source_file_id] = 1
if (kl + 1) % 10000 == 0:
print('File {0}'.format(k), 'Line {0}'.format(kl + 1), source_file)
df = pd.DataFrame(res)
df.index = inputs
print('starting writing to disk...')
df.T.to_csv('output.csv')