为什么pandas.grouby.mean比并行实现快得多

4

我正在一个非常大的数据集上使用pandas grouby平均函数,就像以下这样:

import pandas as pd
df=pd.read_csv("large_dataset.csv")
df.groupby(['variable']).mean() 

看起来该函数没有使用多进程,因此我实现了一个并行版本:

import pandas as pd 
from multiprocessing import Pool, cpu_count 

def meanFunc(tmp_name, df_input): 
    df_res=df_input.mean().to_frame().transpose()
    return df_res 

def applyParallel(dfGrouped, func):
    num_process=int(cpu_count())
    with Pool(num_process) as p: 
        ret_list=p.starmap(func, [[name, group] for name, group in dfGrouped])
    return pd.concat(ret_list)

applyParallel(df.groupby(['variable']), meanFunc)

然而,似乎pandas的实现仍然比我的并行实现快得多。 我正在查看pandas分组的源代码,看到它使用了cython。那是原因吗?
def _cython_agg_general(self, how, alt=None, numeric_only=True,
                        min_count=-1):
    output = {}
    for name, obj in self._iterate_slices():
        is_numeric = is_numeric_dtype(obj.dtype)
        if numeric_only and not is_numeric:
            continue

        try:
            result, names = self.grouper.aggregate(obj.values, how,
                                                   min_count=min_count)
        except AssertionError as e:
            raise GroupByError(str(e))
        output[name] = self._try_cast(result, obj)

    if len(output) == 0:
        raise DataError('No numeric types to aggregate')

    return self._wrap_aggregated_output(output, names)

我猜主要原因是你的代码需要花费大量时间将“本地”Pandas数据帧(C对象)转换为Python对象,然后再转回去。 - AKX
1个回答

4
简短回答 - 如果您想要这些类型案例的并行处理,请使用dask。 您的方法存在缺陷,而dask可以避免这些缺陷。它可能仍然不会更快,但会为您提供最佳选择,并且基本上是pandas的替代品。
较长回答:
1)并行处理固有地增加了开销,因此理想情况下,您要并行处理的操作应该是相当昂贵的。累加数字并不是特别昂贵 - 您是对的,cython在这里被使用,您正在查看的代码是分派逻辑。实际的核心cython在here,它转换为非常简单的c循环。
2)您正在使用多进程 - 这意味着每个进程都需要复制数据。这很昂贵。通常,由于GIL,您必须在Python中执行此操作 - 实际上您可以(并且dask确实可以)在此处使用线程,因为pandas操作在C中并释放GIL。

3) 正如评论中@AKX所指出的 - 在并行化之前进行迭代(... name, group in dfGrouped)也是相对昂贵的 - 它为每个组构建新的子数据帧。原始的pandas算法在原地迭代数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接