Pandas 熊猫:优化我的代码(groupby()/apply())

Pandas 熊猫:优化我的代码(groupby()/apply()),pandas,Pandas,我有一个外形为(RxC)1.5M x 128的数据框。我做了以下工作: 我基于6列执行groupby()。这将创建约8700个子组,每个子组的形状为538 x 122 在每个子组上,我运行apply()。此函数计算子组中每列(即122)每个分类值的百分比频率 因此,我的(比苏多)代码: g=df.groupby(grp\U cols) g[nongrp\u cols].apply(lambda d:d.apply(lambda s:s.value\u counts())/len(d.index

我有一个外形为(RxC)1.5M x 128的数据框。我做了以下工作:

  • 我基于6列执行groupby()。这将创建约8700个子组,每个子组的形状为538 x 122
  • 在每个子组上,我运行apply()。此函数计算子组中每列(即122)每个分类值的百分比频率
  • 因此,我的(比苏多)代码:

    
    g=df.groupby(grp\U cols)
    g[nongrp\u cols].apply(lambda d:d.apply(lambda s:s.value\u counts())/len(d.index))

    代码对我来说运行正常,所以现在我正在分析它以提高性能。apply()函数运行大约需要20-25分钟。我认为问题在于它在每一列(122次)上迭代8700次(每个子组),这可能不是最好的方法(考虑到我对它的编码方式)

    有谁能推荐我可以加快速度的方法吗

    我尝试使用python多处理池(8个进程)将子组分成相等的集合进行处理,但最终得到一些pickle错误


    谢谢。

    pd.DataFrame.groupby.apply确实给了我们很大的灵活性(与agg/filter/transform不同,它允许您将每个子组重塑为任何形状,从538 x 122到N_categories x 122)。但它确实要付出代价:一个接一个地应用灵活的函数,并且缺少矢量化

    我仍然认为解决这个问题的方法是使用多处理。您遇到的pickle错误很可能是因为您在multi_processing_函数中定义了一些函数。规则是,您必须在顶层移动所有函数。请参阅下面的代码

    import pandas as pd
    import numpy as np
    
    # simulate your data with int 0 - 9 for categorical values
    df = pd.DataFrame(np.random.choice(np.arange(10), size=(538, 122)))
    # simulate your groupby operations, not so cracy with 8700 sub-groups, just try 800 groups for illustration
    sim_keys = ['ROW' + str(x) for x in np.arange(800)]
    big_data = pd.concat([df] * 800, axis=0, keys=sim_keys)
    big_data.shape
    
    big_data.shape
    Out[337]: (430400, 122)
    
    # Without multiprocessing
    # ===================================================
    by_keys = big_data.groupby(level=0)
    
    sample_group = list(by_keys)[0][1]
    sample_group.shape
    
    def your_func(g):
        return g.apply(lambda s: s.value_counts()) / len(g.index)
    
    def test_no_multiprocessing(gb, apply_func):
        return gb.apply(apply_func)
    
    %time result_no_multiprocessing = test_no_multiprocessing(by_keys, your_func)
    
    CPU times: user 1min 26s, sys: 4.03 s, total: 1min 30s
    Wall time: 1min 27
    
    这里很慢。让我们使用多处理模块:

    # multiprocessing for pandas dataframe apply
    # ===================================================
    # to void pickle error, must define functions at TOP level, if we move this function 'process' into 'test_with_multiprocessing', it raises a pickle error
    def process(df):
        return df.groupby(level=0).apply(your_func)
    
    def test_with_multiprocessing(big_data, apply_func):
    
        import multiprocessing as mp
    
        p = mp.Pool(processes=8)
        # split it into 8 chunks
        split_dfs = np.array_split(big_data, 8, axis=0)
        # define the mapping function, wrapping it to take just df as input
        # apply to each chunk
        df_pool_results = p.map(process, split_dfs)
    
        p.close()
    
        # combine together
        result = pd.concat(df_pool_results, axis=0)
    
        return result
    
    
    %time result_with_multiprocessing = test_with_multiprocessing(big_data, your_func)
    
    CPU times: user 984 ms, sys: 3.46 s, total: 4.44 s
    Wall time: 22.3 s
    
    现在,它要快得多,尤其是在CPU时间方面。虽然当我们拆分和重新组合结果时会有一些开销,但当使用8核处理器时,它预计比非多处理情况快4-6倍

    最后,检查两个结果是否相同

    import pandas.util.testing as pdt
    
    pdt.assert_frame_equal(result_no_multiprocessing, result_with_multiprocessing)
    

    漂亮地通过测试。

    首先要做的是:g[nongrp\u cols].apply(lambda d:d.apply(lambda s:s.value\u counts(normalize=True)))非常感谢。