汇总大量数据

时间:2018-07-02 09:17:01

标签: python python-3.x nlp hdf5 large-data

我有一个无法解决的问题。我有4个.txt文件,每个文件的大小在30-70GB之间。每个文件包含n-gram条目,如下所示:

blabla1/blabla2/blabla3
word1/word2/word3
...

我想做的是计算每个项目出现的次数,然后将此数据保存到新文件中,例如:

blabla1/blabla2/blabla3  : 1
word1/word2/word3        : 3
...

到目前为止,我的尝试仅仅是将所有条目保存在字典中并对其进行计数,即

entry_count_dict = defaultdict(int)
with open(file) as f:
    for line in f:
        entry_count_dict[line] += 1

但是,使用这种方法会遇到内存错误(我有8GB的可用内存)。数据遵循zipfian分布,例如大多数项目仅出现一次或两次。 条目总数尚不清楚,但(非常)粗略估计是总共大约有1500万个条目。

除此之外,我尝试了h5py,其中所有条目都保存为包含数组[1]的h5py数据集,然后将其更新,例如:

import h5py
import numpy as np

entry_count_dict = h5py.File(filename)
with open(file) as f:
    for line in f:
        if line in entry_count_dict:
            entry_count_file[line][0] += 1
        else:
            entry_count_file.create_dataset(line, 
                                            data=np.array([1]),
                                            compression="lzf")

但是,这种方法很慢。写入速度越来越慢。因此,除非可以提高写入速度,否则这种方法是不可行的。另外,按块处理数据并为每个块打开/关闭h5py文件在处理速度上没有任何显着差异。

我一直在考虑将以某些字母开头的条目保存在单独的文件中,即所有以a开头的条目都保存在a.txt中,依此类推(应该使用defaultdic(int))。 但是,为此,文件必须每个字母重复一次,这对于文件大小(最大= 69GB)而言是不现实的。 也许在遍历文件时,可以打开一个泡菜并将条目保存到字典中,然后关闭泡菜。但是,由于打开,加载和关闭泡菜文件需要花费时间,因此对每个项目执行此操作会大大减慢该过程。

解决此问题的一种方法是在一次通过中对所有条目进行排序,然后遍历排序的文件并按字母顺序对条目进行计数。但是,即使使用linux命令对文件进行排序也非常缓慢:

sort file.txt > sorted_file.txt

而且,由于将整个文件加载到内存中进行排序会导致内存错误,因此我真的不知道如何使用python解决此问题。我对不同的排序算法有一些肤浅的了解,但是它们似乎都要求将要排序的整个对象都加载到内存中。

任何有关如何解决此问题的技巧将不胜感激。

2 个答案:

答案 0 :(得分:0)

有许多用于执行此类操作的算法。它们全都属于mapToScene()的标题。

您在那里所做的“将以某些字母开头的条目保存在单独的文件中”实际上被称为存储桶排序,从理论上讲,它应该更快。尝试使用切片数据集。

或者, 尝试External Sorting,这是DARPA + Anaconda支持的分布式计算库,具有numpy,pandas熟悉的接口,并且类似于Apache-Spark。 (也可以在单台机器上工作) 顺便说一下

我建议尝试 dask.array , 它将大数组切成小数组,并使用阻塞算法实现numpy ndarray接口,以在计算这些大于内存的数据时利用您的所有内核。

答案 1 :(得分:0)

  

我一直在考虑将以某些字母开头的条目保存在单独的文件中,即所有以a开头的条目都保存在a.txt中,依此类推(应使用defaultdic(int)做到)。但是,为此,文件必须每个字母重复一次,鉴于文件大小(最大= 69GB),这是不现实的。

您的思路差不多。您要做的是根据前缀拆分文件-您不必为每个字母重复一次。在awk中这是微不足道的。假设您的输入文件位于名为input的目录中:

mkdir output
awk '/./ {print $0 > ( "output/"  substr($0,0,1))}` input/*

这会将每行追加到以该行的第一个字符命名的文件中(请注意,如果您的行可以以空格开头,这将很奇怪;因为这些是ngram,所以我认为这无关紧要)。您也可以在Python中执行此操作,但是管理文件的打开和关闭有点麻烦。

由于文件已被拆分,因此它们现在应该小得多。您可以对它们进行排序,但实际上没有必要-您可以单独读取文件并使用以下代码获取计数:

from collections import Counter

ngrams = Counter()
for line in open(filename):
    ngrams[line.strip()] += 1
for key, val in ngrams.items():
    print(key, val, sep='\t')

如果文件仍然太大,则可以增加用于存储行的前缀的长度,直到文件足够小为止。