在数千个表之间联合行的可扩展方法

时间:2016-11-11 23:40:42

标签: python csv pandas union

我有大约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')

1 个答案:

答案 0 :(得分:2)

xto1 = lambda x: 1
def read_ex(fn):
    s = pd.read_csv(
        fn, sep=' ', header=None,
        index_col=0, usecols=[0, 1],
        converters={1: xto1},
        names=[None, fn],
        squeeze=True)
    return s

fs = ['example1.csv', 'example2.csv']

pd.concat([read_ex(f) for f in fs], keys=fs).unstack(fill_value=0)

enter image description here