【问题标题】:Need for speed: Slow nested groupbys and applys in Pandas极品飞车:慢速嵌套 groupby 并在 Pandas 中应用
【发布时间】:2014-02-08 10:01:51
【问题描述】:

我正在对 DataFrame 执行复杂的转换。我认为这对 Pandas 来说会很快,但我设法做到这一点的唯一方法是使用一些嵌套的 groupbys 和 apply,使用 lambda 函数,而且速度很慢。似乎应该有内置的、更快的方法。在 n_rows=1000 是 2 秒,但我会做 10^7 行,所以这太慢了。很难解释我们在做什么,所以这里是代码和配置文件,然后我会解释:

n_rows = 1000

d = pd.DataFrame(randint(1,10,(n_rows,8))) #Raw data
dgs = array([3,4,1,8,9,2,3,7,10,8]) #Values we will look up, referenced by index
grps = pd.cut(randint(1,5,n_rows),arange(1,5)) #Grouping

f = lambda x: dgs[x.index].mean() #Works on a grouped Series
g = lambda x: x.groupby(x).apply(f) #Works on a Series
h = lambda x: x.apply(g,axis=1).mean(axis=0) #Works on a grouped DataFrame

q = d.groupby(grps).apply(h) #Slow



824984 function calls (816675 primitive calls) in 1.850 seconds
Ordered by: internal time
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
221770    0.105    0.000    0.105    0.000 {isinstance}
  7329    0.104    0.000    0.217    0.000 index.py:86(__new__)
  8309    0.089    0.000    0.423    0.000 series.py:430(__new__)
  5375    0.081    0.000    0.081    0.000 {method 'reduce' of 'numpy.ufunc' objects}
 34225    0.068    0.000    0.133    0.000 {method 'view' of 'numpy.ndarray' objects}
36780/36779    0.067    0.000    0.067    0.000 {numpy.core.multiarray.array}
  5349    0.065    0.000    0.567    0.000 series.py:709(_get_values)
 985/1    0.063    0.000    1.847    1.847 groupby.py:608(apply)
  5349    0.056    0.000    0.198    0.000 _methods.py:42(_mean)
  5358    0.050    0.000    0.232    0.000 index.py:332(__getitem__)
  8309    0.049    0.000    0.228    0.000 series.py:3299(_sanitize_array)
  9296    0.047    0.000    0.116    0.000 index.py:1341(__new__)
   984    0.039    0.000    0.092    0.000 algorithms.py:105(factorize)

按分组对 DataFrame 行进行分组。对于每个分组,对于每一行,按相同的值进行分组(即,所有的值都为 3,而所有的值都为 4)。对于值分组中的每个索引,在dgs 中查找对应的索引,并取平均值。然后对行分组进行平均。

::呼气::

任何关于如何重新排列以提高速度的建议都将不胜感激。

【问题讨论】:

  • 像这样进行嵌套的嵌套应用/分组不是答案。这几乎是纯 python 代码,你没有利用任何 pandas 的优势。您可能希望在顶层进行分组(或构建多索引),选择要包含的值,然后使用 cythonized 函数应用它。您只想做 1 级 groupby 并申请(在一些非常罕见的情况下除外)。你最终是在做一些向量化的操作,但你是在最低级别向后做的。

标签: python pandas


【解决方案1】:

你可以通过一个多级groupby来做apply和groupby,代码如下:

import pandas as pd
from numpy import array, arange
from numpy.random import randint, seed

seed(42)
n_rows = 1000

d = pd.DataFrame(randint(1,10,(n_rows,8))) #Raw data
dgs = array([3,4,1,8,9,2,3,7,10,8]) #Values we will look up, referenced by index
grps = pd.cut(randint(1,5,n_rows),arange(1,5)) #Grouping

f = lambda x: dgs[x.index].mean() #Works on a grouped Series
g = lambda x: x.groupby(x).apply(f) #Works on a Series
h = lambda x: x.apply(g,axis=1).mean(axis=0) #Works on a grouped DataFrame

print d.groupby(grps).apply(h) #Slow

### my code starts from here ###

def group_process(df2):
    s = df2.stack()
    v = np.repeat(dgs[None, :df2.shape[1]], df2.shape[0], axis=0).ravel()
    return pd.Series(v).groupby([s.index.get_level_values(0), s.values]).mean().mean(level=1)

print d.groupby(grps).apply(group_process)

输出:

               1         2         3         4         5         6         7  \
(1, 2]  4.621575  4.625887  4.775235  4.954321  4.566441  4.568111  4.835664   
(2, 3]  4.446347  4.138528  4.862613  4.800538  4.582721  4.595890  4.794183   
(3, 4]  4.776144  4.510119  4.391729  4.392262  4.930556  4.695776  4.630068   

               8         9  
(1, 2]  4.246085  4.520384  
(2, 3]  5.237360  4.418934  
(3, 4]  4.829167  4.681548  

[3 rows x 9 columns]
               1         2         3         4         5         6         7  \
(1, 2]  4.621575  4.625887  4.775235  4.954321  4.566441  4.568111  4.835664   
(2, 3]  4.446347  4.138528  4.862613  4.800538  4.582721  4.595890  4.794183   
(3, 4]  4.776144  4.510119  4.391729  4.392262  4.930556  4.695776  4.630068   

               8         9  
(1, 2]  4.246085  4.520384  
(2, 3]  5.237360  4.418934  
(3, 4]  4.829167  4.681548  

[3 rows x 9 columns]

大约快 70 倍,但我不知道它是否可以处理 10**7 行。

【讨论】:

  • 仅供参考,这是一个完全可并行化的问题,一旦您拆分组并且您可以说在单独的进程中执行它们,例如 concat(dict([ (g,group_process2(grp)) for g, grp在 d.groupby(grps) ]))
猜你喜欢
  • 2013-06-02
  • 2022-11-04
  • 1970-01-01
  • 2016-02-03
  • 2021-12-04
  • 2021-10-15
  • 2017-12-19
  • 2018-10-31
  • 2013-06-24
相关资源
最近更新 更多