为什么 groupby.diff 这么慢?

Eas*_*sun 5 python pandas

我想计算每个组的系列差异,如下例所示:

\n\n
In [24]: rnd_ser = pd.Series(np.random.randn(5000))\n    ...: com_ser = pd.concat([rnd_ser] * 500, keys=np.arange(500), names=[\'Date\', \'ID\'])\n\nIn [25]: d1 = com_ser.groupby("Date").diff()\n\nIn [26]: d2 = com_ser - com_ser.groupby("Date").shift()\n\nIn [27]: np.allclose(d1.fillna(0), d2.fillna(0))\nOut[27]: True\n
Run Code Online (Sandbox Code Playgroud)\n\n

有两种方法可以解决这个问题,但是第一种方法性能较差:

\n\n
In [30]: %timeit d1 = com_ser.groupby("Date").diff()\n616 ms \xc2\xb1 5.62 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n\nIn [31]: %timeit d2 = com_ser - com_ser.groupby("Date").shift()\n95 ms \xc2\xb1 326 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是预期的还是错误?

\n\n

我的环境的详细信息:

\n\n
In [23]: pd.show_versions()\n\nINSTALLED VERSIONS\n------------------\ncommit: None\npython: 3.7.1.final.0\npython-bits: 64\nOS: Windows\nOS-release: 10\nmachine: AMD64\nprocessor: Intel64 Family 6 Model 158 Stepping 10, GenuineIntel\nbyteorder: little\nLC_ALL: None\nLANG: None\nLOCALE: None.None\n\npandas: 0.23.4\npytest: 3.9.3\npip: 18.1\nsetuptools: 40.5.0\nCython: 0.29\nnumpy: 1.15.3\nscipy: 1.1.0\npyarrow: None\nxarray: None\nIPython: 7.1.1\nsphinx: 1.8.1\npatsy: 0.5.1\ndateutil: 2.7.5\npytz: 2018.7\nblosc: None\nbottleneck: 1.2.1\ntables: 3.4.4\nnumexpr: 2.6.8\nfeather: None\nmatplotlib: 3.0.1\nopenpyxl: 2.5.9\nxlrd: 1.1.0\nxlwt: 1.3.0\nxlsxwriter: 1.1.2\nlxml: 4.2.5\nbs4: 4.6.3\nhtml5lib: 1.0.1\nsqlalchemy: 1.2.12\npymysql: None\npsycopg2: None\njinja2: 2.10\ns3fs: None\nfastparquet: None\npandas_gbq: None\npandas_datareader: None\n
Run Code Online (Sandbox Code Playgroud)\n

T. *_*arf 3

FWIW,我在我的机器上看到类似的数字

\n\n
%timeit d1 = com_ser.groupby("Date").diff()\n523 ms \xc2\xb1 32.7 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n\n%timeit d2 = com_ser - com_ser.groupby("Date").shift()\n80.8 ms \xc2\xb1 2.31 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

Pandas 的实施diff()似乎很慢groupby()

\n\n

例如,如果我制作一个大系列

\n\n

big_ser = pd.Series(np.random.randn(int(1e7)))

\n\n

然后比较移位和减法与Series.diff()

\n\n
%timeit big_ser - big_ser.shift()\n46.3 ms \xc2\xb1 789 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n\n%timeit big_ser.diff()\n41.6 ms \xc2\xb1 488 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么实现之间的时间是相同的。当您查看内部源代码时,如下所示Series.diff 它会在注释中明确说明,如下所示

\n\n
def diff(arr, n, axis=0):\n    """\n    difference of n between self,\n    analogous to s-s.shift(n)\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以我认为它必须有一些groupby特定的开销diff()

\n