我是python的新手。我一直在考虑将下面的代码用于parellel调用,其中doj值的列表是在lambda的帮助下格式化的,
m_df[['doj']] = m_df[['doj']].apply(lambda x: formatdoj(*x), axis=1)
def formatdoj(doj):
doj = str(doj).split(" ")[0]
doj = datetime.strptime(doj, '%Y' + "-" + '%m' + "-" + "%d")
return doj
由于列表有数百万条记录,因此格式化所有内容所需的时间会花费很多时间。
如何在python中调用parellel函数,类似于p#中的Parellel.Foreach?
答案 0 :(得分:1)
我认为在你的情况下使用并行计算有点过分。缓慢来自代码,而不是来自使用单个处理器。我将在某些步骤中向您展示如何加快速度,猜测您是否正在使用Pandas数据帧以及您的数据帧包含哪些内容(请坚持使用SO指南并包含完整的工作示例!!)
对于我的测试,我使用了以下随机数据帧,行数为100k(缩放时间最长可达到您的情况):
N=int(1e5)
m_df = pd.DataFrame([['{}-{}-{}'.format(y,m,d)]
for y,m,d in zip(np.random.randint(2007,2019,N),
np.random.randint(1,13,N),
np.random.randint(1,28,N))],
columns=['doj'])
现在这是你的代码:
tstart = time()
m_df[['doj']] = m_df[['doj']].apply(lambda x: formatdoj(*x), axis=1)
print("Done in {:.3f}s".format(time()-tstart))
在我的机器上,它运行在大约5.1秒。它有几个问题。第一个是您使用数据框而不是系列,尽管您只在一列上工作,并创建一个无用的lambda函数。只需做:
m_df['doj'].apply(formatdoj)
将时间减少到1.6秒。同时使用' +'加入字符串在python中很慢,您可以将formatdoj更改为:
def faster_formatdoj(doj):
return datetime.strptime(doj.split()[0], '%Y-%m-%d')
m_df['doj'] = m_df['doj'].apply(faster_formatdoj)
这不是一个很大的改进,但确实减少了1.5s。如果你需要加入真实的字符串(因为它们不是固定的),而是使用'-'.join('%Y','%m','%d')
,那就更快了。
但真正的瓶颈来自于使用datetime.strptime很多次。它本质上是一个缓慢的命令 - 日期是一个笨重的事情。另一方面,如果你有数百万的约会,并且假设它们从人类开始以来就没有统一传播,那么它们很可能是大规模复制的。所以你应该如何真正做到这一点:
tstart = time()
# Create a new column with only the first word
m_df['doj_split'] = m_df['doj'].apply(lambda x: x.split()[0])
converter = {
x: faster_formatdoj(x) for x in m_df['doj_split'].unique()
}
m_df['doj'] = m_df['doj_split'].apply(lambda x: converter[x])
# Drop the column we added
m_df.drop(['doj_split'], axis=1, inplace=True)
print("Done in {:.3f}s".format(time()-tstart))
大约0.2 / 0.3s,比原始实现快10倍以上。
毕竟,如果你仍然在慢速运行,你可以考虑并行工作(而不是分别并行化第一个" split"指令,也许是apply-lambda部分,否则你&#39 ; d创建许多不同的"转换器"字典使增益无效)。但我认为这是最后一步而不是第一步......
[编辑]:最初在最后一个代码框的第一步,我使用了m_df['doj_split'] = m_df['doj'].str.split().apply(lambda x: x[0])
,它功能相同,但比m_df['doj_split'] = m_df['doj'].apply(lambda x: x.split()[0])
慢一点。我不完全确定原因,可能是因为它主要使用两个函数而不是一个函数。
答案 1 :(得分:0)
最好的办法是使用dask
。 Dask有一个data_frame类型,您可以使用它来创建类似的数据帧,但是,在执行计算功能时,您可以使用num_worker
参数指定核心数。这将并行化任务
答案 2 :(得分:0)
由于我不确定你的例子,我会使用multiprocessing
库再给你一个:
# -*- coding: utf-8 -*-
import multiprocessing as mp
input_list = ["str1", "str2", "str3", "str4"]
def format_str(str_input):
str_output = str_input + "_test"
return str_output
if __name__ == '__main__':
with mp.Pool(processes = 2) as p:
result = p.map(format_str, input_list)
print (result)
现在,假设你要映射一个带有多个参数的函数,然后你应该使用starmap()
:
# -*- coding: utf-8 -*-
import multiprocessing as mp
input_list = ["str1", "str2", "str3", "str4"]
def format_str(str_input, i):
str_output = str_input + "_test" + str(i)
return str_output
if __name__ == '__main__':
with mp.Pool(processes = 2) as p:
result = p.starmap(format_str, [(input_list, i) for i in range(len(input_list))])
print (result)
不要忘记将池放在if __name__ == '__main__':
内,并且multiprocessing
在spyder
(或其他)等IDE中不起作用,因此您需要运行cmd中的脚本。
要保留结果,您可以将它们保存到文件中,也可以在最后使用os.system("pause")
(Windows)或Linux上的input()
保持cmd打开。
使用python进行多处理是一种相当简单的方法。