HOWTO · Python
Python 中 NumPy 数组的滑动平均值
本教程演示了如何在 python 中计算 numpy 数组的滑动平均值
本页内容
简单移动平均是对固定大小窗口内连续数值求平均。对于 NumPy 数组,可以直接用 numpy.convolve 或 numpy.cumsum 计算;也可以使用 bottleneck.move_mean,或通过 pandas 的 rolling().mean() 计算。窗口大小为 4,表示每个结果都概括相邻的 4 个输入值。移动平均可以平滑短期波动,让趋势更容易观察;加权移动平均则会给较新的数据更大权重。
下面的示例数据包含 12 个值。第一种方法会为每个完整的 4 值窗口返回一个结果,因此会得到 9 个值。应根据每个平均值需要概括的观测数量来选择窗口大小;改变窗口大小会同时改变平滑程度和有效窗口结果的数量。
使用 numpy.convolve 计算 NumPy 数组的移动平均
numpy.convolve 用于计算线性卷积。这里,第二个数组由 4 个 1 组成,因此每个卷积结果都是一个 4 值窗口的总和。将每个总和除以 4,就得到该窗口的平均值。valid 模式只返回完全落在输入范围内的窗口,因此 12 个输入值和大小为 4 的窗口会产生 9 个结果。这些结果从左到右与各窗口对应:第一个结果是输入位置 0 到 3 的平均值,最后一个是位置 8 到 11 的平均值。
import numpy as np
def moving_average(values, window):
weights = np.ones(window) / window
return np.convolve(values, weights, mode="valid")
data = np.array([10, 5, 8, 9, 15, 22, 26, 11, 15, 16, 18, 7])
print(moving_average(data, 4))
[ 8. 9.25 13.5 18. 18.5 18.5 17. 15. 14. ]
使用 numpy.cumsum 计算 NumPy 数组的移动平均
利用累积和,可以用窗口末尾的累积总和减去窗口之前的累积总和,得到该窗口的总和。第一个窗口之前没有可供相减的累积总和,因此函数从第一个完整窗口开始切片。这样得到的 9 个平均值与 numpy.convolve 示例相同。如果你想了解如何通过运行总和逐个计算窗口总和,这种方法很实用;将每个窗口总和除以窗口大小,即可得到平均值。
import numpy as np
def moving_average(values, window):
cumulative = np.cumsum(values, dtype=float)
cumulative[window:] -= cumulative[:-window]
return cumulative[window - 1:] / window
data = np.array([10, 5, 8, 9, 15, 22, 26, 11, 15, 16, 18, 7])
print(moving_average(data, 4))
[ 8. 9.25 13.5 18. 18.5 18.5 17. 15. 14. ]
使用 bottleneck 模块计算移动平均
bottleneck 包提供了 move_mean,可用于计算数组的移动平均。默认情况下,窗口必须完整,因此最初 3 个位置还没有对应的 4 值平均值;第一个平均值出现在索引 3 处。结果会保留与输入相同的长度,开头这些位置以 nan 表示。示例会在 Bottleneck 可用时使用它;如果没有安装这个可选包,也提供了一个简短的纯 Python 等效实现。当你需要让每个输入值都对应一个结果位置时,可以采用这种形式;开头的 nan 表示完整窗口尚未形成。
from importlib.util import find_spec
data = [10, 5, 8, 9, 15, 22, 26, 11, 15, 16, 18, 7]
window = 4
if find_spec("bottleneck") is not None:
import bottleneck as bn
result = bn.move_mean(data, window=window).tolist()
else:
result = [float("nan")] * (window - 1)
result.extend(
sum(data[start:start + window]) / window
for start in range(len(data) - window + 1)
)
print(result)
[nan, nan, nan, 8.0, 9.25, 13.5, 18.0, 18.5, 18.5, 17.0, 15.0, 14.0]
使用 pandas 模块计算移动平均
对于存储在 pandas Series 中的时间序列数据,可以调用 rolling(4).mean() 计算每个完整 4 值窗口的平均值。默认情况下,Series 会保留原有长度和索引。开头 3 个条目是 NaN,因为这些位置还没有足够的值组成完整窗口;第一个计算出的平均值与索引 3 对齐。此处的数值与其他示例相同,而 Series 索引便于将每个结果对应回原始观测值。如果窗口大小来自变量,也可以将该变量传给 rolling()。
import pandas as pd
data = [10, 5, 8, 9, 15, 22, 26, 11, 15, 16, 18, 7]
series = pd.Series(data)
print(series.rolling(4).mean())
0 NaN
1 NaN
2 NaN
3 8.00
4 9.25
5 13.50
6 18.00
7 18.50
8 18.50
9 17.00
10 15.00
11 14.00
dtype: float64