Pandas中groupby的last存在性能问题

6

我有一个数据帧,有两列,超过十万个元素。

In [43]: df.head(10)
Out[43]:
                    localtime                 ref
4  2014-04-02 12:00:00.273537  139058754703810577
5  2014-04-02 12:00:02.223501  139058754703810576
6  2014-04-02 12:00:03.518817  139058754703810576
7  2014-04-02 12:00:03.572082  139058754703810576
8  2014-04-02 12:00:03.572444  139058754703810576
9  2014-04-02 12:00:03.572571  139058754703810576
10 2014-04-02 12:00:03.573320  139058754703810576
11 2014-04-02 12:00:09.278517  139058754703810576
14 2014-04-02 12:00:20.942802  139058754703810577
15 2014-04-02 12:01:13.410607  139058754703810576

[10 rows x 2 columns]

In [44]: df.dtypes
Out[44]:
localtime    datetime64[ns]
ref                   int64
dtype: object

In [45]: len(df)
Out[45]: 111743

In [46]: g = df.groupby('ref')

如果我请求组中的最后一个元素,函数就会卡住!
In [47]: %timeit g.last()

我在6分钟后结束了它的运行; top 显示整个时间CPU占用率达到100%。
如果我明确请求localtime列,这样至少可以返回结果,虽然对于数量如此之少的元素来说,它仍然似乎非常慢。
In [48]: %timeit g['localtime'].last()
1 loops, best of 3: 4.6 s per loop

我是否漏掉了什么?这里使用的是pandas 0.13.1。


这个问题出现在datetime64类型中。假设我直接从文件中读取:

In [1]: import pandas as pd

In [2]: df = pd.read_csv('so.csv')

In [3]: df.dtypes
Out[3]:
localtime    object
ref           int64
dtype: object

In [4]: %timeit df.groupby('ref').last()
10 loops, best of 3: 28.1 ms per loop

object类型可以正常工作。但是,如果我强制转换我的时间戳,一切都会失控:

In [5]: df.localtime = pd.to_datetime(df.localtime)

In [6]: df.dtypes
Out[6]:
localtime    datetime64[ns]
ref                   int64
dtype: object

In [7]: %timeit df.groupby('ref').last()

情节变得扑朔迷离。
使用Jeff的建议进行无数据文件复制:
In [70]: rng = pd.date_range('20130101',periods=20,freq='s')

In [71]: df = pd.DataFrame(dict(timestamp = rng.take(np.random.randint(0,20,size=100000)), value = np.random.randint(0,100,size=100000)*1000000))

In [72]: %timeit df.groupby('value').last()
1 loops, best of 3: 332 ms per loop

然而,如果我改变随机整数的范围,问题会再次出现!
In [73]: df = pd.DataFrame(dict(timestamp = rng.take(np.random.randint(0,20,size=100000)), value = np.random.randint(0,100000,size=100000)*1000))

In [74]: %timeit df.groupby('value').last()                                                           

我只是增加了第二个randint()high参数,这意味着groupby()将具有更大的长度。这样可以在没有数据文件的情况下重现我的错误。
请注意,如果我放弃使用datetime64类型,则没有问题:
In [12]: df = pd.DataFrame(dict(timestamp = np.random.randint(0,20,size=100000), value = np.random.randint(0,100000,size=100000)*1000))

In [13]: %timeit df.groupby('value').last()
100 loops, best of 3: 14.4 ms per loop

所以罪魁祸首在于对datetime64进行last()的缩放。

尝试使用“g.tail(1)”命令;你能发布你的文件吗?(以及你用来读取它的read_csv代码)(如果是其他格式也可以,只需发布代码)。 - Jeff
1个回答

4

一定有什么奇怪的事情发生了....在0.13.1(和主分支)中看起来没问题。请发布你的文件链接,我会帮你查看。

In [3]: rng = date_range('20130101',periods=20,freq='s')

In [4]: df = DataFrame(dict(timestamp = rng.take(np.random.randint(0,20,size=100000)), value = np.random.randint(0,100,size=100000)*1000000))

In [5]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
timestamp    100000 non-null datetime64[ns]
value        100000 non-null int64
dtypes: datetime64[ns](1), int64(1)
In [6]: %timeit df.groupby('value')['timestamp'].last()
100 loops, best of 3: 9.07 ms per loop

In [7]: %timeit df.groupby('value')['timestamp'].tail(1)
100 loops, best of 3: 16.3 ms per loop

好的,这里是解释:

使用np.random.randint(0,100,size=100000)作为值,创建了100个组, 而np.random.randint(0,100000,size=100000)则创建了更多的组(在我的例子中有大约63000个)。

.last(在<0.14版本中)会隐式地选择non-nan值中的最后一个。这种na测试并不便宜,因此它的性能缩放效果很差(并且对于每个组都在python空间中执行)。

另一方面,tail(1)(在<0.14版本中)不会检查这一点,因此性能要好得多(并使用cython路由来获取结果)。

在0.14版本中,它们将是相同的(即使您像这样做:nth(-1,dropna='any'),这将复制last在这里所做的操作,这也是以更好的性能方式完成的(感谢@Andy Hayden)。

总之,在<0.14版本中使用tail(1)


嗯,我可以得到你的结果。而奇怪的是,如果我将我的数据框保存到文件中,然后再读取回来,性能就非常好。不幸的是,我不能发布我的原始数据,因为这是专有市场数据。我会继续调查,并在发现一些东西时发布更新。哦,tail(1) 很好用;只有 last() 有问题。 - chrisaycock
你在读取时使用了“parse_dates=True”吗?但是然后使用“pd.to_datetime”可以正确读取它吗?(字段中是否生成了“NaT”?例如,无法解析的条目) - Jeff
你使用的是32位操作系统吗? - Jeff
@Jeff:你能否发布一个链接,指向代码中 last 进行测试的位置? - unutbu
https://github.com/pydata/pandas/blob/master/pandas/core/groupby.py#L129; 和相关问题:https://github.com/pydata/pandas/issues/6732。 - Jeff
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接