pandas: groupby apply using numba

6 人关注

使用pandas v1.1.0。

在pandas文档中,有一个关于如何使用numba来加速 rolling.apply() 操作的好例子 here

import pandas as pd
import numpy as np
def mad(x):
    return np.fabs(x - x.mean()).mean()
df = pd.DataFrame({"A": np.random.randn(100_000)},
                  index=pd.date_range('1/1/2000', periods=100_000, freq='T')
).cumsum()
df.rolling(10).apply(mad, engine="numba", raw=True)

我想调整它,使之适用于groupby操作。

df['day'] = df.index.day
df.groupby('day').agg(mad)

works fine.

df.groupby('day').agg(mad, engine='numba')

错误,并给出了

---------------------------------------------------------------------------
NumbaUtilError                            Traceback (most recent call last)
<ipython-input-21-ee23f1eec685> in <module>
----> 1 df.groupby('day').agg(mad, engine='numba')
~\AppData\Local\Continuum\anaconda3\envs\ds-cit-dev\lib\site-packages\pandas\core\groupby\generic.py in aggregate(self, func, engine, engine_kwargs, *args, **kwargs)
    940         if maybe_use_numba(engine):
--> 941             return self._python_agg_general(
    942                 func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
    943             )
~\AppData\Local\Continuum\anaconda3\envs\ds-cit-dev\lib\site-packages\pandas\core\groupby\groupby.py in _python_agg_general(self, func, engine, engine_kwargs, *args, **kwargs)
   1069             if maybe_use_numba(engine):
-> 1070                 result, counts = self.grouper.agg_series(
   1071                     obj,
   1072                     func,
~\AppData\Local\Continuum\anaconda3\envs\ds-cit-dev\lib\site-packages\pandas\core\groupby\ops.py in agg_series(self, obj, func, engine, engine_kwargs, *args, **kwargs)
    624         if maybe_use_numba(engine):
--> 625             return self._aggregate_series_pure_python(
    626                 obj, func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
    627             )
~\AppData\Local\Continuum\anaconda3\envs\ds-cit-dev\lib\site-packages\pandas\core\groupby\ops.py in _aggregate_series_pure_python(self, obj, func, engine, engine_kwargs, *args, **kwargs)
    682         if maybe_use_numba(engine):
--> 683             numba_func, cache_key = generate_numba_func(
    684                 func, engine_kwargs, kwargs, "groupby_agg"
    685             )
~\AppData\Local\Continuum\anaconda3\envs\ds-cit-dev\lib\site-packages\pandas\core\util\numba_.py in generate_numba_func(func, engine_kwargs, kwargs, cache_key_str)
    215     nopython, nogil, parallel = get_jit_arguments(engine_kwargs)
    216     check_kwargs_and_nopython(kwargs, nopython)
--> 217     validate_udf(func)
    218     cache_key = (func, cache_key_str)
    219     numba_func = NUMBA_FUNC_CACHE.get(
~\AppData\Local\Continuum\anaconda3\envs\ds-cit-dev\lib\site-packages\pandas\core\util\numba_.py in validate_udf(func)
    177         or udf_signature[:min_number_args] != expected_args
    178     ):
--> 179         raise NumbaUtilError(
    180             f"The first {min_number_args} arguments to {func.__name__} must be "
    181             f"{expected_args}"
NumbaUtilError: The first 2 arguments to mad must be ['values', 'index']

我猜想,对于engine=numba,它所期望的数据会略有不同。

python
pandas
pandas-groupby
numba
bodo
Ray Bell
Ray Bell
发布于 2020-08-05
3 个回答
Ehsan
Ehsan
发布于 2020-08-05
已采纳
0 人赞同

你有没有试过Bodo来做这个?它建立在Numba之上,直接支持Pandas。比如说。

pip install bodo
import pandas as pd
import numpy as np
import bodo
def mad(x):
    return np.fabs(x - x.mean()).mean()
np.random.seed(0)
df = pd.DataFrame({"A": np.random.randn(100_000)},
                  index=pd.date_range('1/1/2000', periods=100_000, freq='T')
).cumsum()
@bodo.jit(distributed=False)
def f(df):
    df['day'] = df.index.day
    df2 = df.groupby('day').agg(mad)
    return df2
df2 = f(df)
print(df2)

这个例子似乎太小了,无法从编译中获益,但这可能有助于你的真实用例。

Caio Castro
Caio Castro
发布于 2020-08-05
0 人赞同

我自己也遇到了这个问题。显然,要使用pandas + numba引擎,你必须 required 来实现格式为 f(value, index) 的自定义函数。

根据文档(GroupBy.transform)。

如果选择 "numba "引擎,该函数必须是一个用户定义的 函数,在函数签名中,value和index分别作为第一和第二参数 分别作为函数签名中的第一和第二参数。每个组的索引将被 传递给用户定义的函数,并且可以选择使用。

我有一个简单的函数 f(x) 返回一个 int ,我想在groupby里面使用。要让它与numba一起工作,只需将函数修改为 f(values, index) ,这样numba例程就会有一个有效的参数来传递索引给函数。

前一个函数(工作正常,但在numba下不正常)。

def equal_weight(arr) -> int:
    returns a float of 1/n where 'n' is the number of rows
    return 1 / len(arr)

新功能,与numba引擎兼容。

def equal_weight(values, index) -> int:
    returns a float of 1/n where 'n' is the number of rows
    return 1 / len(values)