我想使用称为file_list
的函数处理存储在get_scores_dataframe
中的大量csv文件。此函数采用存储在另一个列表中的第二个自变量phenotypes
。然后,该函数将结果写回到csv文件。我设法使用ProcessPoolExecutor()
来并行化此任务,因此它可以正常工作。
with concurrent.futures.ProcessPoolExecutor() as executor:
phenotypes = [phenotype for i in range(len(file_list))]
futures = executor.map(get_scores_dataframe, file_list, phenotypes,
chunksize=25)
filenames = executor.map(os.path.basename, file_list)
for future, filename in zip(futures, filenames):
futures.to_csv(os.path.join(f'{output_path}',f'{filename}.csv'),
index = False)
如您所见,我为此使用了上下文管理器,并且在上下文管理器中使用了方法map()
,可以在其中设置选项chunksize
。但是,我希望程序在完成处理每个数据帧时写入csv文件。似乎上下文管理器会等到所有作业完成后再将结果写入csv文件。
您知道我该如何实现吗?
答案 0 :(得分:2)
首先,executor.map
不返回Future
实例,因此变量futures
的命名不正确。它确实返回一个迭代器,该迭代器产生将get_scores_dataframe
依次应用于file_list
的每个元素的返回值。其次,看看接下来如何使用它们,这些返回值似乎是输入文件(与输入参数可能相同或不同)–由于缺少所示代码,因此无法确定。同样,使用进程池map
函数而不是内置的map
函数来获取文件名参数的基本名称似乎有些大材小用。最后,在您的代码中,它不是futures.to_csv
,而是future.to_csv
。因此,我对您的代码本该如何工作感到困惑。
如果您修改函数get_scores_dataframe
以返回由数据框和原始传递的filename参数组成的元组,那么我们可以使用as_competed
按完成顺序处理结果:
from concurrent.futures import as_completed
import multiprocessing
with concurrent.futures.ProcessPoolExecutor(multiprocessing.cpu_count() - 1) as executor:
futures = [executor.submit(get_scores_dataframe, file, phenotype) for file in file_list]
for future in as_completed(futures):
# it is assumed return value is tuple: (data frame, original filename argument):
df, file = future.result()
csv_filename = os.path.basename(file)
df.to_csv(os.path.join(f'{output_path}', f'{csv_filename}.csv'), index = False)
现在,通过使用submit
,您将失去“分批”提交工作的能力。我们可以将multiprocessing.Pool
与imap_unordered
一起使用。但是imap_unordered
只能将单个参数传递给worker函数。因此,如果您能够修改工作程序以更改参数的顺序,我们可以将phenotype
设为第一个,并使用partial
(see manual):
import multiprocessing
from functools import partial
POOL_SIZE = multiprocessing.cpu_count() - 1 # leave 1 for main process
def compute_chunksize(iterable_size):
if iterable_size == 0:
return 0
chunksize, extra = divmod(iterable_size, POOL_SIZE * 4)
if extra:
chunksize += 1
return chunksize
with multiprocessing.Pool(POOL_SIZE) as pool:
chunksize = compute_chunksize(len(file_list))
worker = partial(get_scores_dataframe, phenotype)
# it is assumed that start_processing returns a tuple: (data frame, original filename argument)
for df, file in pool.imap_unordered(worker, file_list, chunksize):
csv_filename = os.path.basename(file)
df.to_csv(os.path.join(f'{output_path}', f'{csv_filename}.csv'), index = False)