熊猫多处理适用

时间:2014-11-06 16:15:30

标签: python pandas multiprocessing

我尝试将多处理与pandas数据帧一起使用,即将数据帧拆分为8个部分。使用apply(每个部分在不同的过程中处理)对每个部分应用一些功能。

编辑: 这是我最终找到的解决方案:

import multiprocessing as mp
import pandas.util.testing as pdt

def process_apply(x):
    # do some stuff to data here

def process(df):
    res = df.apply(process_apply, axis=1)
    return res

if __name__ == '__main__':
    p = mp.Pool(processes=8)
    split_dfs = np.array_split(big_df,8)
    pool_results = p.map(aoi_proc, split_dfs)
    p.close()
    p.join()

    # merging parts processed by different processes
    parts = pd.concat(pool_results, axis=0)

    # merging newly calculated parts to big_df
    big_df = pd.concat([big_df, parts], axis=1)

    # checking if the dfs were merged correctly
    pdt.assert_series_equal(parts['id'], big_df['id'])

9 个答案:

答案 0 :(得分:7)

基于作者解决方案的通用版本,可以在每个函数和数据框上运行它:

from multiprocessing import  Pool
from functools import partial
import numpy as np

def parallelize(data, func, num_of_processes=8):
    data_split = np.array_split(data, num_of_processes)
    pool = Pool(num_of_processes)
    data = pd.concat(pool.map(func, data_split))
    pool.close()
    pool.join()
    return data

def run_on_subset(func, data_subset):
    return data_subset.apply(func, axis=1)

def parallelize_on_rows(data, func, num_of_processes=8):
    return parallelize(data, partial(run_on_subset, func), num_of_processes)

下面这行:

df.apply(some_func, axis=1)

将成为:

parallelize_on_rows(df, some_func) 

答案 1 :(得分:4)

由于我没有太多的数据脚本,这是猜测,但我建议使用p.map代替apply_async进行回调。

p = mp.Pool(8)
pool_results = p.map(process, np.array_split(big_df,8))
p.close()
p.join()
results = []
for result in pool_results:
    results.extend(result)

答案 2 :(得分:2)

这是我发现有用的一些代码。自动将数据帧拆分为您拥有的任意数量的 CPU 内核。

import pandas as pd
import numpy as np
import multiprocessing as mp

def parallelize_dataframe(df, func):
    num_processes = mp.cpu_count()
    df_split = np.array_split(df, num_processes)
    with mp.Pool(num_processes) as p:
        df = pd.concat(p.map(func, df_split))
    return df

def parallelize_function(df):
    df[column_output] = df[column_input].apply(example_function)
    return df

def example_function(x):
    x = x*2
    return x

运行:

df_output = parallelize_dataframe(df, parallelize_function)

答案 3 :(得分:1)

您可以使用https://github.com/nalepae/pandarallel,如以下示例所示:

from pandarallel import pandarallel
from math import sin

pandarallel.initialize()

def func(x):
    return sin(x**2)

df.parallel_apply(func, axis=1)

答案 4 :(得分:1)

要使用所有(物理或逻辑)内核,可以尝试使用mapply替代swifterpandarallel

您可以在初始化时设置核心数量(以及分块行为):

import pandas as pd
import mapply

mapply.init(n_workers=-1)

def process_apply(x):
    # do some stuff to data here

def process(df):
    # spawns a pathos.multiprocessing.ProcessPool if sensible
    res = df.mapply(process_apply, axis=1)
    return res

默认情况下(n_workers=-1),程序包使用系统上所有可用的物理CPU。如果您的系统使用超线程(通常会显示两倍的物理CPU),mapply将产生一个额外的工作程序,以将多处理池的优先级设置为高于系统上的其他进程。

您还可以改用所有逻辑核心(请注意,像这样的CPU绑定进程将在争夺物理CPU,这可能会减慢您的操作速度):

import multiprocessing
n_workers = multiprocessing.cpu_count()

# or more explicit
import psutil
n_workers = psutil.cpu_count(logical=True)

答案 5 :(得分:0)

当我使用multiprocessing.map()将函数应用于大型数据帧的不同块时,我也遇到了同样的问题。

我只想添加几点,以防其他人遇到与我相同的问题。

  1. 请记得添加if __name__ == '__main__':
  2. .py文件中执行该文件,如果您使用ipython/jupyter notebook,则无法运行multiprocessing(我的情况也是如此,但我不知道)

答案 6 :(得分:0)

这对我来说很好:

rows_iter = (row for _, row in df.iterrows())

with multiprocessing.Pool() as pool:
    df['new_column'] = pool.map(process_apply, rows_iter)

答案 7 :(得分:0)

安装Pyxtension可以简化并行地图的使用,并且可以这样使用:

from pyxtension.streams import stream

big_df = pd.concat(stream(np.array_split(df, multiprocessing.cpu_count())).mpmap(process))

答案 8 :(得分:0)

我最终使用concurrent.futures.ProcessPoolExecutor.map代替了multiprocessing.Pool.map,而这花费了316微秒的时间来完成一些代码,而这些代码又花费了12秒。