我使用以下代码将CSV文件拆分为多个块(来自here)
def worker(chunk):
print len(chunk)
def keyfunc(row):
return row[0]
def main():
pool = mp.Pool()
largefile = 'Counseling.csv'
num_chunks = 10
start_time = time.time()
results = []
with open(largefile) as f:
reader = csv.reader(f)
reader.next()
chunks = itertools.groupby(reader, keyfunc)
while True:
# make a list of num_chunks chunks
groups = [list(chunk) for key, chunk in
itertools.islice(chunks, num_chunks)]
if groups:
result = pool.map(worker, groups)
results.extend(result)
else:
break
pool.close()
pool.join()
但是,无论我选择使用多少块,看起来块总是保持不变。例如,无论我选择有1个还是10个块,我总是在处理样本文件时获得此输出。理想情况下,我希望将文件分块以便公平分配。
注意,我正在分块的真实文件超过1300万行,这就是为什么我要一块一块地处理它。这是必须的!
6
7
1
...
1
1
94
--- 0.101687192917 seconds ---
答案 0 :(得分:12)
每https://wiki.jenkins-ci.org/display/JENKINS/Subversion+Plugin#SubversionPlugin-Postcommithook,
我们希望每个进程都在一个10000行的块上运行。这并不难
去做;请参阅下面的iter/islice
食谱。但是,使用
pool.map(worker, ten_thousand_row_chunks)
pool.map
会尝试将所有块放入任务队列
马上。如果这需要比可用内存更多的内存,那么你得到一个
MemoryError
。 (注意:pool.imap
the
comments。)
相反,我们需要在每个块的片段上迭代地调用pool.map
。
import itertools as IT
import multiprocessing as mp
import csv
def worker(chunk):
return len(chunk)
def main():
# num_procs is the number of workers in the pool
num_procs = mp.cpu_count()
# chunksize is the number of lines in a chunk
chunksize = 10**5
pool = mp.Pool(num_procs)
largefile = 'Counseling.csv'
results = []
with open(largefile, 'rb') as f:
reader = csv.reader(f)
for chunk in iter(lambda: list(IT.islice(reader, chunksize*num_procs)), []):
chunk = iter(chunk)
pieces = list(iter(lambda: list(IT.islice(chunk, chunksize)), []))
result = pool.map(worker, pieces)
results.extend(result)
print(results)
pool.close()
pool.join()
main()
每个chunk
将包含文件中最多chunksize*num_procs
行。
这是足够的数据,可以为池中的所有工作人员提供一些工作,但不会太大而导致MemoryError - 提供的chunksize
未设置得太大。
然后将每个chunk
分成几部分,每部分由最多一部分组成
文件中的chunksize
行。然后将这些部分发送到pool.map
。
iter(lambda: list(IT.islice(iterator, chunksize)), [])
如何运作:
这是将迭代器分组为长度为chunksize的块的习惯用法。 让我们看看它是如何工作的:
In [111]: iterator = iter(range(10))
请注意,每次调用IT.islice(iterator, 3)
时,都会有3个项目的新块
从迭代器中切掉:
In [112]: list(IT.islice(iterator, 3))
Out[112]: [0, 1, 2]
In [113]: list(IT.islice(iterator, 3))
Out[113]: [3, 4, 5]
In [114]: list(IT.islice(iterator, 3))
Out[114]: [6, 7, 8]
当迭代器中剩余的项目少于3个时,只返回剩余的项目:
In [115]: list(IT.islice(iterator, 3))
Out[115]: [9]
如果你再次打电话,你会得到一个空列表:
In [116]: list(IT.islice(iterable, 3))
Out[116]: []
lambda: list(IT.islice(iterator, chunksize))
是一个在调用时返回list(IT.islice(iterator, chunksize))
的函数。这是一个"单线"这相当于
def func():
return list(IT.islice(iterator, chunksize))
最后,iter(callable, sentinel)
返回另一个迭代器。此迭代器产生的值是callable返回的值。它继续产生值,直到callable返回一个等于sentinel的值。所以
iter(lambda: list(IT.islice(iterator, chunksize)), [])
将继续返回值list(IT.islice(iterator, chunksize))
,直到该值为空列表:
In [121]: iterator = iter(range(10))
In [122]: list(iter(lambda: list(IT.islice(iterator, 3)), []))
Out[122]: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
答案 1 :(得分:6)
首先,如果记录尚未在键列上排序,则itertools.groupby将没有任何实际意义。 此外,如果您的要求只是将csv文件分块为预定数量的行并将其提供给工作人员,那么您不必执行所有这些操作。
一个简单的实现将是:
import csv
from multiprocessing import Pool
def worker(chunk):
print len(chunk)
def emit_chunks(chunk_size, file_path):
lines_count = 0
with open(file_path) as f:
reader = csv.reader(f)
chunk = []
for line in reader:
lines_count += 1
chunk.append(line)
if lines_count == chunk_size:
lines_count = 0
yield chunk
chunk = []
else:
continue
if chunk : yield chunk
def main():
chunk_size = 10
gen = emit_chunks(chunk_size, 'c:/Temp/in.csv')
p = Pool(5)
p.imap(worker, gen)
print 'Completed..'
*编辑:更改为pool.imap而不是pool.map