Pandas高效计算VWAP

16

我有下面的代码,用它可以通过三行Pandas代码计算加权平均价格。

import numpy as np
import pandas as pd
from pandas.io.data import DataReader
import datetime as dt

df = DataReader(['AAPL'], 'yahoo', dt.datetime(2013, 12, 30), dt.datetime(2014, 12, 30))
df['Cum_Vol'] = df['Volume'].cumsum()
df['Cum_Vol_Price'] = (df['Volume'] * (df['High'] + df['Low'] + df['Close'] ) /3).cumsum()
df['VWAP'] = df['Cum_Vol_Price'] / df['Cum_Vol']

我正在尝试练习一种不使用 cumsum() 函数的编码方法。我正在尝试找到一种能够在单次通过中给出 VWAP 列的解决方案。我已经尝试了下面这行代码,使用了 .apply() 函数。逻辑是正确的,但问题在于我无法将第 n 行的值存储起来以便在第 (n+1) 行中使用。您如何在 pandas 中处理这个问题 - 只需使用外部元组或字典来临时存储累积值吗?

df['Cum_Vol']= np.nan
df['Cum_Vol_Price'] = np.nan
# calculate running cumulatives by apply - assume df row index is 0 to N
df['Cum_Vol'] = df.apply(lambda x: df.iloc[x.name-1]['Cum_Vol'] + x['Volume'] if int(x.name)>0 else x['Volume'], axis=1)

是否有一种一次性的解决方案来解决上述问题?

编辑:

我的主要动机是了解底层发生了什么。因此,这主要是为了练习而不是任何有效的原因。我相信在大小为N的Series上每个cumsum的时间复杂度都为N(?)。所以我想知道,是否可以在一次通行中计算两者 - 沿着这条路线。很高兴接受对此的答案 - 而不是工作代码。


使用 apply 的速度比您第一种方法要慢得多,顺便说一下。 - EdChum
@EdChum,谢谢。你有没有不使用cumsum的替代方案? - Zhubarb
目前还不行,cumsum是一种矢量化方法,apply无法超越它。 - EdChum
@JohnE,我的主要动机是要了解发生了什么。因此,这主要是为了练习而不是任何有效的原因。我认为在大小为N的Series上每个cumsum的时间复杂度为N。所以我想知道,是否可以在一次遍历中计算两个独立的cumsum而不是运行两个单独的cumsum - 类似于这个。非常乐意接受回答 - 而不是工作代码。 - Zhubarb
2个回答

20

快速编辑:只是想感谢 John 的原始发布 :)

您可以通过对numpy的版本进行 @jit 优化来获得更快的结果:

@jit
def np_vwap():
    return np.cumsum(v*(h+l)/2) / np.cumsum(v)

使用上述vwap版本,每次循环所需的时间为74.5 µs,而采用这种方法,每次循环所需的时间只有50.9 µs


1
谢谢你的改进!我刚刚自己计时,没有得到那么大的速度提升,但你的方法肯定更快。我认为numba随着时间的推移已经在与numpy结合方面变得更好了。 - JohnE

19

一次性完成与一行完成开始变得有点语义化。那这个区别怎么样:您可以使用1行pandas,1行numpy或几行numba来完成它。

from numba import jit

df=pd.DataFrame( np.random.randn(10000,3), columns=['v','h','l'] )

df['vwap_pandas'] = (df.v*(df.h+df.l)/2).cumsum() / df.v.cumsum()

@jit
def vwap():
    tmp1 = np.zeros_like(v)
    tmp2 = np.zeros_like(v)
    for i in range(0,len(v)):
        tmp1[i] = tmp1[i-1] + v[i] * ( h[i] + l[i] ) / 2.
        tmp2[i] = tmp2[i-1] + v[i]
    return tmp1 / tmp2

v = df.v.values
h = df.h.values
l = df.l.values

df['vwap_numpy'] = np.cumsum(v*(h+l)/2) / np.cumsum(v)

df['vwap_numba'] = vwap()

时间:

%timeit (df.v*(df.h+df.l)/2).cumsum() / df.v.cumsum()  # pandas
1000 loops, best of 3: 829 µs per loop

%timeit np.cumsum(v*(h+l)/2) / np.cumsum(v)            # numpy
10000 loops, best of 3: 165 µs per loop

%timeit vwap()                                         # numba
10000 loops, best of 3: 87.4 µs per loop

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