Python 以多线程方式从非常大的文本文件创建字典

Python 以多线程方式从非常大的文本文件创建字典,python,multithreading,text,multiprocessing,Python,Multithreading,Text,Multiprocessing,我有一个巨大的文本文件,我想为它创建一个字典(计数器)。 目前,我正在使用以下代码执行此操作: with open(file_name) as input_doc: for line in input_doc: for word in line.strip().split(): vocab[word] += 1 但是,由于文件很大,需要花费很多时间。 因此,我正在寻找一种更快的方法 想到的最直接的解决方案是在一个列表中存储一组行(小批量),分别处理每个批(与其他批并行)

我有一个巨大的文本文件,我想为它创建一个字典(计数器)。 目前,我正在使用以下代码执行此操作:

with open(file_name) as input_doc:
for line in input_doc:
    for word in line.strip().split():
        vocab[word] += 1
但是,由于文件很大,需要花费很多时间。 因此,我正在寻找一种更快的方法

想到的最直接的解决方案是在一个列表中存储一组行(小批量),分别处理每个批(与其他批并行),最后合并结果。 通过这种方式,我们可以节省大量时间,并且可以在主线程从文件中读取下一批行时处理以前看到的批(并行)

比如:

buffer_size = 1000
buff = []
vocab = Counter()
number_of_sentences = 1
with open(file_name) as input_doc:
    for line in input_doc:
        if number_of_sentences % buffer_size == 0:
            vocab += update_dictionary(buff) ### Here I should create and call a new thread to work on the new batch
            buff = []
        else
            buff.append(line)
            number_of_sentences += 1
这里,update_dictionary()方法读取给定列表中的所有句子并更新其本地字典。一旦完成,它的本地字典应该与全局字典合并。 我试了几个小时,但不幸的是,因为我从未用Python实现多线程代码,所以我无法使它工作。 你能帮我实现这个想法吗

非常感谢。

模块使用进程而不是线程来解决问题。您可以将任务提交到池以并行处理。当您向线程池提交任务时,它将返回一个表示正在运行的任务的对象(这称为future)。您可以通过这种方式启动多个任务。准备好获取任务结果后,可以调用future.result()。下面是一个并行获取列表中所有字符串的总长度的示例:

from concurrent.futures import ThreadPoolExecutor
from collections import defaultdict

def runTask(lines):
    counts = defaultdict(int)
    for line in lines:
        for word in line.split():
            counts[word] += 1

    return counts

pool = ThreadPoolExecutor(4)
futures = []
chunkSize = 4
lines = []

with open("test.txt") as f:
    for line in f:
        if len(lines) == chunkSize:
            futures.append(pool.submit(runTask, lines))
            lines = [] 
        else:
            lines.append(line)

    if len(lines) > 0:
        futures.append(pool.submit(runTask, lines))

# Sum up totals
finalCount = defaultdict(int)
for f in futures:
    result = f.result()
    for k in result:
        finalCount[k] += result[k]

for word in finalCount:
    print("{0}: {1}".format(word, finalCount[word]))
这是第一次尝试帮助您入门。

该模块使用进程而不是线程来解决问题。您可以将任务提交到池以并行处理。当您向线程池提交任务时,它将返回一个表示正在运行的任务的对象(这称为future)。您可以通过这种方式启动多个任务。准备好获取任务结果后,可以调用future.result()。下面是一个并行获取列表中所有字符串的总长度的示例:

from concurrent.futures import ThreadPoolExecutor
from collections import defaultdict

def runTask(lines):
    counts = defaultdict(int)
    for line in lines:
        for word in line.split():
            counts[word] += 1

    return counts

pool = ThreadPoolExecutor(4)
futures = []
chunkSize = 4
lines = []

with open("test.txt") as f:
    for line in f:
        if len(lines) == chunkSize:
            futures.append(pool.submit(runTask, lines))
            lines = [] 
        else:
            lines.append(line)

    if len(lines) > 0:
        futures.append(pool.submit(runTask, lines))

# Sum up totals
finalCount = defaultdict(int)
for f in futures:
    result = f.result()
    for k in result:
        finalCount[k] += result[k]

for word in finalCount:
    print("{0}: {1}".format(word, finalCount[word]))

这是第一次尝试帮助您入门。

这听起来像是所有Map Reduce文献中的规范字数示例。如果这不是一个一次性的分析,而且你的输入文件真的很大(就像大数据一样),你可以考虑利用Hadoop或Skp. Spark上的第一个示例有一些您可以复制的内容,几乎是一字不差的:

text_file = sc.textFile("file:///path/to/your/input/file")
counts = text_file.flatMap(lambda line: line.strip().split()) \
                  .map(lambda word: (word, 1)) \
                  .reduceByKey(lambda a, b: a + b)
vocab = dict(counts.collect())

下载Spark并使其在本地工作,然后根据需要扩展EMR中的问题(文件系统使用S3)。

这听起来像所有Map Reduce文献中的规范字数示例。如果这不是一个一次性的分析,而且你的输入文件真的很大(就像大数据一样),你可以考虑利用Hadoop或Skp. Spark上的第一个示例有一些您可以复制的内容,几乎是一字不差的:

text_file = sc.textFile("file:///path/to/your/input/file")
counts = text_file.flatMap(lambda line: line.strip().split()) \
                  .map(lambda word: (word, 1)) \
                  .reduceByKey(lambda a, b: a + b)
vocab = dict(counts.collect())

下载Spark并使其在本地工作,然后根据需要扩展EMR中的问题(使用S3处理文件系统)。

只要您使用
cpython
多线程就不会有帮助。全局解释器锁(GIL)一次只允许执行一个线程。还有其他并行版本的python可能会有所帮助。通过使用
counter.update(word-for-word-in-line.strip().split())
替换
for-word-in…
部分,并使用大缓冲区打开文件,您可以获得一点速度。您看过多处理库了吗。每个进程都可以用来计算文件的一部分中的单词,并拥有自己独特的中间字典。然后,您可以使用多处理队列与主进程共享数据。我不认为重写程序以使用编译语言是一种选择吗?(因为如果速度是你所要寻找的,编译的语言,比如C++,可以快速完成同样的任务,即使单个线程只受硬盘驱动器速度的限制,而不是内存计算的开销),只要你使用<代码> cPython < /Cord>,多线程就不会对你有帮助。全局解释器锁(GIL)一次只允许执行一个线程。还有其他并行版本的python可能会有所帮助。通过使用
counter.update(word-for-word-in-line.strip().split())
替换
for-word-in…
部分,并使用大缓冲区打开文件,您可以获得一点速度。您看过多处理库了吗。每个进程都可以用来计算文件的一部分中的单词,并拥有自己独特的中间字典。然后,您可以使用多处理队列与主进程共享数据。我不认为重写程序以使用编译语言是一种选择吗?(因为如果速度是你所要寻找的,编译的语言,如C++,可以做同样的工作,足够快,即使一个线程将只限于硬盘的速度,而不是由内存计算的开销)是通过管道传递到子进程的参数?如果是这样,这只是将从文件中读取线替换为从管道中读取线。不管怎样,哈基姆需要在他的顶级流程中创建字典,所以我认为这不会有什么帮助,尽管它很酷。我不确定。作为一项改进,我首先将文件偏移量传递给子进程,这样顶级进程就不必读取所有行。我实施了您的方法,但它不仅没有提高性能,甚至使性能变得最差。我注意到,即使我将线程数设置为8,CPU使用率也只有140%,而且在管理线程时似乎有很多过载。因此,通过比较两种方法(串行和并行)的时间,我注意到所需的时间显著增加(串行和并行设置分别为2分钟和5分钟左右)。对不起,我给了一个“-”