加速自定义聚合函数

6

我很简单的设置如下:市场数据(ticks)在一个名为df的pandas数据框中,格式如下:

index period ask bid
00:00:00.126 42125 112.118 112.117
00:00:00.228 42125 112.120 112.117
00:00:00.329 42125 112.121 112.120
00:00:00.380 42125 112.123 112.120
00:00:00.432 42125 112.124 112.121
00:00:00.535 41126 112.124 112.121
00:00:00.586 41126 112.122 112.121
00:00:00.687 41126 112.124 112.121
00:00:01.198 41126 112.124 112.120
00:00:01.737 41126 112.124 112.121
00:00:02.243 41126 112.123 112.121

现在我使用pandas.groupy来聚合时间段。
g=df.groupby('period')

您可以轻松获取每个时期的最低和最高价格,例如:

import numpy as np
res=g.agg({'ask': [np.amax, np.amin]})

这个也相当快。现在,我还想知道每个时期的第一个和最后一个价格。这就是问题所在。当然,我可以这样做:

res=g.agg({'ask': lambda x: x[0]})

基本上,它能够正常工作,但对于大型数据集来说速度非常慢。 Python函数调用的呼叫开销非常高。

有没有人知道类似于np.amax的numpy函数,可以返回一组中的第一个或最后一个元素?我找不到这样的函数。iloc [0]行不通,因为它是一个对象的方法,因此我无法将其作为函数传递给g.agg,因为在这个阶段我还没有该对象(这就是需要lambda的原因)。

现在,我并不是懒惰,我尝试使用cython自己完成这个任务。

import numpy as np
cimport numpy as np

cpdef double first(np.ndarray array_series):
    return array_series[0]

但是Pandas不接受这个作为聚合函数,因为它传递的是pd.core.series对象而不是np.ndarray。(尽管一个从另一个导出,但编译器并不认识。)

有没有人知道如何编写一个Cython函数,可以接受Pandas series而没有Python调用开销?


你尝试过使用 df.groupby('period').first()df.groupby('period').last() 吗? - EdChum
谢谢,这是个好提示。它能用,但我不能将first()函数传递给g.agg(...), 对吗?我更喜欢这样做,因为我想一次应用许多不同的聚合函数(amin,amax,first,...)。我猜使用它然后手动组装我的最终数据集会是一种解决方法。 - user5507059
可以的,请看我的回答。 - EdChum
2个回答

3
如果我理解正确,你可以使用firstlast
In [270]:
g=df.groupby('period')
res=g.agg({'ask': [np.amax, np.amin, 'first', 'last']})
res

Out[270]:
            ask                           
           amax     amin    first     last
period                                    
41126   112.124  112.122  112.124  112.123
42125   112.124  112.118  112.118  112.124

你很棒。谢谢!! - user5507059
但是更进一步,如果我想要一个非标准的自定义聚合函数,我能将其Cython化吗? - user5507059
可能吧,但我不是Cython专家。 - EdChum
使用自定义函数的方法是将其Cythonize/numbize,参见文档。虽然在分组表达式上执行此操作有点棘手。 - Jeff

1

另一种方法是简单地重新采样并使用OHLC (open=first,close=last,high=max,low=min)

In [56]: df = DataFrame({'A' : np.arange(10), 'B' : pd.date_range('20130101',periods=5).tolist()*2})

In [57]: df
Out[57]: 
   A          B
0  0 2013-01-01
1  1 2013-01-02
2  2 2013-01-03
3  3 2013-01-04
4  4 2013-01-05
5  5 2013-01-01
6  6 2013-01-02
7  7 2013-01-03
8  8 2013-01-04
9  9 2013-01-05

In [58]: df.set_index('B').resample('D',how='ohlc')
Out[58]: 
              A               
           open high low close
B                             
2013-01-01    0    5   0     5
2013-01-02    1    6   1     6
2013-01-03    2    7   2     7
2013-01-04    3    8   3     8
2013-01-05    4    9   4     9

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