Parellel函数调用python

时间:2018-06-13 08:28:59

标签: python

我是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?

3 个答案:

答案 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__':内,并且multiprocessingspyder(或其他)等IDE中不起作用,因此您需要运行cmd中的脚本。

要保留结果,您可以将它们保存到文件中,也可以在最后使用os.system("pause")(Windows)或Linux上的input()保持cmd打开。

使用python进行多处理是一种相当简单的方法。