【问题标题】:Performance issues with groupby's last in pandasgroupby 在 pandas 中的最后一个性能问题
【发布时间】:2014-04-03 18:22:44
【问题描述】:

我有一个包含两列和十万多元素的 DataFrame。

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

我有什么遗漏吗?这是熊猫 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 )(或者如果其他格式可以,只需发布​​代码)。

标签: python pandas


【解决方案1】:

一定是发生了什么奇怪的事情……在 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 (in non-nan 值。这个 na 测试并不便宜,因此扩展性能很差(并且在每个组的 python 空间中完成)。

另一方面,tail(1)(在

在 0.14 中,这些将是相同的(即使您这样做:nth(-1,dropna='any') 将复制 last 在这里所做的事情,这样做的方式是有更好的性能。(感谢@Andy海登)。

底线是在 tail(1)。

【讨论】:

  • 嗯,我可以得到你的结果。奇怪的是,如果我将我的 DataFrame 保存到一个文件并读回它,那么性能非常好。不幸的是,我无法发布我的原始数据,因为它是专有市场数据。当我发现一些东西时,我会进行更多调查并发布更新。哦,tail(1) 返回就好了;只是 last() 有问题。
  • 新信息:如果我保存我的 DF 并读回它,localtime 列将变为 object 类型。获取last() 非常快。但是,如果我将localtime 转换回datetime64[ns](通过pd.to_datetime),那么last() 会变慢!
  • 你是parse_dates=True 正在阅读吗?但是pd.to_datetime 读起来好吗? (他们的NaT 是在字段中生成的吗?(例如无法解析的条目)
  • 我更新了我的问题,增加了几个步骤来重复问题。如果我通过parse_dates=['localtime'],也会出现问题。没有空日期时间。
  • 我无法复制这个(在 master 或 0.13.1 上)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-07
  • 1970-01-01
  • 1970-01-01
  • 2021-03-31
  • 2013-05-30
  • 2021-08-12
  • 1970-01-01
相关资源
最近更新 更多