如何将结果从Multiprocessing.Pool传递到csv?

时间:2014-06-27 20:28:29

标签: python multithreading csv

我有一个python进程(2.7),它接受一个键,进行一堆计算并返回结果列表。这是一个非常简化的版本。

我正在使用多处理来创建线程,因此可以更快地处理它。但是,我的生产数据有几百万行,每个循环需要更长的时间才能完成。我最后一次运行这个循环每个循环需要6分钟才能完成,而在开始时它需要一秒或更短的时间。我认为这是因为所有线程都将结果添加到结果集中并且继续增长直到它包含所有记录。

是否可以使用多处理将每个线程(列表)的结果流式传输到csv或批处理结果集中,以便在一定数量的行之后写入csv?

对于加快或优化方法的任何其他建议将不胜感激。

import numpy as np
import pandas as pd
import csv
import os
import multiprocessing
from multiprocessing import Pool

global keys
keys = [1,2,3,4,5,6,7,8,9,10,11,12]

def key_loop(key):
    test_df = pd.DataFrame(np.random.randn(1,4), columns=['a','b','c','d'])
    test_list = test_df.ix[0].tolist()
    return test_list

if __name__ == "__main__":
    try:
        pool = Pool(processes=8)      
        resultset = pool.imap(key_loop,(key for key in keys) )

        loaddata = []
        for sublist in resultset:
            loaddata.append(sublist)

        with open("C:\\Users\\mp_streaming_test.csv", 'w') as file:
            writer = csv.writer(file)
            for listitem in loaddata:
                writer.writerow(listitem)
        file.close

        print "finished load"
    except:
        print 'There was a problem multithreading the key Pool'
        raise

3 个答案:

答案 0 :(得分:4)

这是一个巩固Eevee和我提出的建议的答案

import numpy as np
import pandas as pd
import csv
from multiprocessing import Pool

keys = [1,2,3,4,5,6,7,8,9,10,11,12]

def key_loop(key):
    test_df = pd.DataFrame(np.random.randn(1,4), columns=['a','b','c','d'])
    test_list = test_df.ix[0].tolist()
    return test_list

if __name__ == "__main__":
    try:
        pool = Pool(processes=8)      
        resultset = pool.imap(key_loop, keys, chunksize=200)

        with open("C:\\Users\\mp_streaming_test.csv", 'w') as file:
            writer = csv.writer(file)
            for listitem in resultset:
                writer.writerow(listitem)

        print "finished load"
    except:
        print 'There was a problem multithreading the key Pool'
        raise

同样,这里的变化是

  1. 直接迭代resultset,而不是先将其不必要地复制到列表中。
  2. keys列表直接提供给pool.imap,而不是从中创建生成器理解。
  3. 提供比默认值1更大的chunksizeimap。较大的chunksize可以降低传递keys内的值所需的进程间通信的成本池中的子流程,keyschunksize非常大时(就像你的情况一样)。您应该尝试使用{{1}}的不同值(尝试大于200的值,如5000等)并查看它如何影响性能。我用200做了一个疯狂的猜测,虽然它肯定比1好。

答案 1 :(得分:2)

以下非常简单的代码将许多工作人员的数据收集到一个CSV文件中。工作者获取一个键并返回一个行列表。父级使用多个工作人员一次处理多个键。完成每个键后,父级将按顺序将输出行写入CSV文件。

小心订单。如果每个工作人员直接写入CSV文件,它们将会出现故障或相互踩踏。让每个工作人员写入自己的CSV文件会很快,但之后需要将所有数据文件合并在一起。

import csv, multiprocessing, sys

def worker(key):
    return [ [key, 0], [key+1, 1] ]


pool = multiprocessing.Pool()   # default 1 proc per CPU
writer = csv.writer(sys.stdout)

for resultset in pool.imap(worker, [1,2,3,4]):
    for row in resultset:
        writer.writerow(row)

输出

1,0
2,1
2,0
3,1
3,0
4,1
4,0
5,1

答案 2 :(得分:0)

我敢打赌,使用附加功能立即处理大型结构是导致它变慢的原因。我通常做的是,我打开尽可能多的文件作为核心,并使用模数立即写入每个文件,这样,如果你将它们全部导入同一个文件,那么这些流不会造成麻烦(写错误),也不试图存储大量数据。可能不是最好的解决方案,但真的很容易。最后,您只需合并结果。

在运行开始时定义:

num_cores = 8
file_sep = ","
outFiles = [open('out' + str(x) + ".csv", "a") for x in range(num_cores)]

然后在key_loop函数中:

def key_loop(key):
    test_df = pd.DataFrame(np.random.randn(1,4), columns=['a','b','c','d'])
    test_list = test_df.ix[0].tolist()
    outFiles[key % num_cores].write(file_sep.join([str(x) for x in test_list]) 
                                    + "\n")

之后,不要忘记关闭:[x.close() for x in outFiles]

改进:

  • 迭代评论中提到的块。一次写入/处理1行将比写入块慢得多。

  • 处理错误(关闭文件)

  • 重要提示:我不确定"键的含义"变量,但那里的数字将不允许模数确保你有每个进程写入每个单独的流(12个键,模8将使2个进程写入同一个文件)