我如何在 NumPy 中获得指数加权移动平均线,就像 pandas 中的以下内容一样?
import pandas as pd
import pandas_datareader as pdr
from datetime import datetime
# Declare variables
ibm = pdr.get_data_yahoo(symbols='IBM', start=datetime(2000, 1, 1), end=datetime(2012, 1, 1)).reset_index(drop=True)['Adj Close']
windowSize = 20
# Get PANDAS exponential weighted moving average
ewm_pd = pd.DataFrame(ibm).ewm(span=windowSize, min_periods=windowSize).mean().as_matrix()
print(ewm_pd)
我用 NumPy 尝试了以下操作
import numpy as np
import pandas_datareader as pdr
from datetime import datetime
# From this post: http://stackoverflow.com/a/40085052/3293881 by @Divakar
def strided_app(a, L, S): # Window len = L, Stride len/stepsize = S
nrows = ((a.size - L) // S) + 1
n = a.strides[0]
return np.lib.stride_tricks.as_strided(a, shape=(nrows, L), strides=(S * n, n))
def numpyEWMA(price, windowSize):
weights = np.exp(np.linspace(-1., 0., windowSize))
weights /= weights.sum()
a2D = strided_app(price, windowSize, 1)
returnArray = np.empty((price.shape[0]))
returnArray.fill(np.nan)
for index in (range(a2D.shape[0])):
returnArray[index + windowSize-1] = np.convolve(weights, a2D[index])[windowSize - 1:-windowSize + 1]
return np.reshape(returnArray, (-1, 1))
# Declare variables
ibm = pdr.get_data_yahoo(symbols='IBM', start=datetime(2000, 1, 1), end=datetime(2012, 1, 1)).reset_index(drop=True)['Adj Close']
windowSize = 20
# Get NumPy exponential weighted moving average
ewma_np = numpyEWMA(ibm, windowSize)
print(ewma_np)
但结果与 pandas 中的结果不同。
是否有更好的方法直接在 NumPy 中计算指数加权移动平均值并获得与 pandas.ewm().mean()
完全相同的结果?
在 pandas 解决方案的 60,000 个请求中,我得到大约 230 秒。我确信使用纯 NumPy 可以显着减少这种情况。
原文由 RaduS 发布,翻译遵循 CC BY-SA 4.0 许可协议
更新于 08/06/2019
用于大输入的纯 NUMPY、快速和矢量化解决方案
out
就地计算参数,dtype
参数,索引order
参数此函数等效于 pandas 的
ewm(adjust=False).mean()
,但速度更快。ewm(adjust=True).mean()
(熊猫的默认值)可以在结果的开头产生不同的值。我正在努力将adjust
功能添加到此解决方案。当输入太大时, @Divakar 的回答 会导致浮点精度问题。 This is because
(1-alpha)**(n+1) -> 0
whenn -> inf
andalpha -> 1
, leading to divide-by-zero’s andNaN
values popping up in the calculation.这是我最快的解决方案,没有精度问题,几乎完全矢量化。它变得有点复杂,但性能非常好,尤其是对于非常大的输入。不使用就地计算(可以使用
out
参数,节省内存分配时间):100M 元素输入向量需要 3.62 秒,100K 元素输入向量需要 3.2ms,5000 元素需要 293µs在相当旧的 PC 上输入向量(结果会因不同的alpha
/row_size
值而异)。一维 ewma 函数:
二维 ewma 函数:
用法:
只是一个小费
很容易为给定的
alpha
计算“窗口大小”(技术上指数平均值具有无限的“窗口”),具体取决于该窗口中数据对平均值的贡献。例如,这对于选择由于边界效应将结果的多少部分视为不可靠非常有用。该线程中使用的
alpha = 2 / (window_size + 1.0)
关系(来自 pandas 的 ‘span’ 选项)是上述函数(使用sum_proportion~=0.87
)的逆函数的非常粗略的近似值。alpha = 1 - np.exp(np.log(1-sum_proportion)/window_size)
更准确(熊猫的“半衰期”选项等于此公式sum_proportion=0.5
)。在以下示例中,
data
表示连续噪声信号。cutoff_idx
是result
中的第一个位置,其中至少 99% 的值取决于data
中的单独值(即取决于小于-1-% 0]).直到cutoff_idx
的数据被排除在最终结果之外,因为它过于依赖data
中的第一个值,因此可能会扭曲平均值。为了说明上面解决的问题,你可以运行它几次,注意红线经常出现的错误开始,它在
cutoff_idx
之后被跳过:请注意
cutoff_idx==window
因为 alpha 是使用window_size()
函数的反函数设置的,具有相同的sum_proportion
。这类似于 pandas 应用ewm(span=window, min_periods=window)
的方式。