[英]How to parallelize the row wise Pandas dataframe's apply() method
我有以下代码:
import pandas as pd
import time
def enrich_str(str):
val1 = f'{str}_1'
val2 = f'{str}_2'
val3 = f'{str}_3'
time.sleep(3)
return val1, val2, val3
def enrich_row(passed_row):
col_name = str(passed_row['colName'])
my_string = str(passed_row[col_name])
val1, val2, val3 = enrich_str(my_string)
passed_row['enriched1'] = val1
passed_row['enriched2'] = val2
passed_row['enriched3'] = val3
return passed_row
df = pd.DataFrame({'numbers': [1, 2, 3, 4, 5], 'colors': ['red', 'white', 'blue', 'orange', 'red']},
columns=['numbers', 'colors'])
df['colName'] = 'colors'
tic = time.perf_counter()
enriched_df = df.apply(enrich_row, col_name='colors', axis=1)
toc = time.perf_counter()
print(f"{df.shape[0]} rows enriched in {toc - tic:0.4f} seconds")
enriched_df
获取 output dataframe 需要 15 秒,如下所示:
现在我想在我的机器上使用多个线程并行化丰富操作。 我探索了很多解决方案,例如Dask
、 numba
,但它们对我来说似乎都不是直截了当的。
然后我偶然发现了multiprocessing
库及其pool.imaps()
方法。 所以我尝试运行以下代码:
import multiprocessing as mp
tic = time.perf_counter()
pool = mp.Pool(5)
result = pool.imap(enrich_row, df.itertuples(), chunksize=1)
pool.close()
pool.join()
toc = time.perf_counter()
print(f"{df.shape[0]} rows enriched in {toc - tic:0.4f} seconds")
result
大约需要 2 秒, result
不是 Pandas dataframe。 我不知道我哪里错了。
我建议您使用 pathos fork的multiprocessing
,因为它可以更好地处理数据帧的酸洗。 imap
返回一个迭代器,而不是 DataFrame,因此您必须将其转换回来:
def enrich_row(row_tuple):
passed_row = row_tuple[1]
col_name = str(passed_row['colName'])
my_string = str(passed_row[col_name])
val1, val2, val3 = enrich_str(my_string)
passed_row['enriched1'] = val1
passed_row['enriched2'] = val2
passed_row['enriched3'] = val3
return passed_row
df = pd.DataFrame({'numbers': [1, 2, 3, 4, 5], 'colors': ['red', 'white', 'blue', 'orange', 'red']},
columns=['numbers', 'colors'])
df['colName'] = 'colors'
from pathos.multiprocessing import Pool
tic = time.perf_counter()
result = Pool(8).imap(enrich_row, df.iterrows(), chunksize=1)
df = pd.DataFrame(result)
toc = time.perf_counter()
print(f"{df.shape[0]} rows enriched in {toc - tic:0.4f} seconds")
print(df)
请注意,我正在使用df.iterrows()
它返回元组(row_number, row)
的迭代器,所以我修改enrich_row
来处理这种格式。
我接受了@albert 的回答,因为它适用于 Linux。 无论如何,我发现Dask 数据框的apply()
方法非常简单。 正如我在之前的评论中提到的,起初该操作不是在 120 行的数据集上并行执行的。 后来发现120行只用到了Dask dataframe的一个分区。 因此,进行重新分区以获得所需的并行性就足够了。 这是一个使用 Dask 的代码示例(它会引发一些奇怪的警告......)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.