【问题标题】:Groupby Mutate Performance in PandasPandas 中的 Groupby 变异性能
【发布时间】:2018-01-07 03:37:44
【问题描述】:

我经常发现自己尝试使用 R 等效于 groupby 然后 mutate,但正如许多人指出的那样,简单地使用 groupbyapply 会遇到严重的性能问题。所以我的问题是,在 pandas 中对数据框进行分组的最佳(最高性能)方法是什么,然后根据该组中的某些条件,根据一些计算添加一个新列?

(我进行了搜索和搜索,但没有找到任何关于如何使用 numpy 在 pandas 中对自定义函数进行矢量化的指南/步骤。所有类似问题的答案都是针对具体情况的,不能很好地概括。)

示例数据:

df
Out[17]: 
             ID   ID2        col1        col2       col3    value
0             1     J       333.5       333.3      333.4      cat    
1             1     S       333.5       333.3      333.8      
2             2     J       333.7       333.3      333.8      cat
3             2     S       333.7       333.3      333.4      dog
4             3     L       333.7       333.8      333.9      
5             3     D       333.8       333.8      333.9      
6             4     S       333.8       333.6      333.7      cat
7             4     J       333.8       333.2      333.8      
8             4     J       333.8       333.7      333.9      
9             4     L       333.8       333.3      333.4      cat

这里有一些例子,我经常遇到:

1) apply 函数根据条件分组,将这些结果与原始数据帧一起返回。

df.groupby(by=['ID']).apply(myfunc)

def myfunc(group):
    group['new_col'] = len(group.query('''ID2=='T' & (col1>=col3 | px<=col2)''').unique())
    return group

2) 与 1) 类似,但仅根据某些条件更新一个现有列,然后将该结果与原始数据帧一起返回。

df.groupby(by=['ID']).apply(update_func)

def update_func(group):
     if 'S' in group['ID2'].values:
          group.loc[(group['value']=='cat'), 'other_column'] = False
     return group

【问题讨论】:

  • 你有什么具体的例子吗?这是一个开放式问题,被认为是本论坛的广泛问题。
  • @ScottBoston 我发布了几个示例,它们在当前形式中是否过于宽泛?
  • 是的,如果您有输入和预期输出,这会有所帮助。这是我的看法。
  • 在示例 1 中,您的函数返回值应仅为 len(group... .unique()),然后在您的函数中调用 df['new_col'] = df.groupby(by='ID').apply(myfunc)。返回值而不是整个数据框,然后将这些值设置为调用中的新列。
  • 在示例 2 中,我认为您正在尝试过滤数据框。使用 groupbyfilter.... 尝试这样的操作:df.groupby('ID').filter(lambda x: x.ID2.isin(['S']).any()).query('value == "cat"')

标签: pandas performance numpy vectorization pandas-groupby


【解决方案1】:

对于第一个示例,我使用 numpy 运算符改进了函数,并且按照 @ScottBoston 的建议,我将函数更改为仅返回值,然后将它们映射回我的原始数据框:

def my_func(group):
    mask = np.logical_and(group.ID2 == 'J', (np.logical_or((group.col1 >= group.col3), (group.col1 <= group.col2))))
    return len(group[mask].col1.unique())

dict = df.groupby(by=['ID'], sort=False).apply(my_func).to_dict() #This is a bit slow

df['new_col'] = df['ID'].map(dict) #This is fast

最慢的部分仍然是必须对每个组进行操作的numpy 条件的应用。如果有一种方法可以并行操作每一个,那将是理想的,因为不需要像现在这样按顺序执行。

【讨论】:

    【解决方案2】:

    我认为您不需要在 groupby 中进行遮罩,看看这是否对您更有效。

    d1 = df.assign(mask=np.logical_and(df.ID2 == 'J', (np.logical_or((df.col1 >= df.col3), (df.col1 <= df.col2)))))
    
    dict = d1.groupby('ID').apply(lambda x: x.loc[x['mask'],'col1'].size).to_dict()
    

    【讨论】:

    • 似乎更快,但运行 %timeit 给出了以下消息:The slowest run took 26.13 times longer than the fastest. This could mean that an intermediate result is being cached.1 loop, best of 3: 4.02 s per loop
    • 没关系,当我重新运行它时,它没有给出任何奇怪的信息。如果您对第二个示例有任何类似的策略,请告诉我!谢谢:)
    • seoncd 策略已在上述问题下的 cmets 中列出。试试这个说法。
    • 一旦我分组并应用了一个自定义函数来更新该组中的多个单元格值,那么将这个子集 df 更新或重新组合回原始子集的最佳方法是什么?
    • 我必须查看您的原始数据和结果才能确定。你能用这些信息创建一个新问题吗?
    猜你喜欢
    • 1970-01-01
    • 2021-08-12
    • 2019-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-21
    • 2020-06-05
    • 2020-06-19
    相关资源
    最近更新 更多