HOWTO · Python
Python 中 NumPy 陣列的滑動平均值
本教程演示瞭如何在 python 中計算 numpy 陣列的滑動平均值
本頁內容
簡單移動平均是對固定大小的連續數值視窗取平均。對 NumPy 陣列而言,可以直接使用 numpy.convolve 或 numpy.cumsum 計算;bottleneck.move_mean 和搭配 rolling().mean() 的 pandas 則提供以套件為基礎的替代方式。視窗大小為 4,表示每個結果都彙整 4 個相鄰的輸入值。移動平均能平滑短期波動,讓趨勢更容易辨識;加權移動平均則會讓較近期的資料占更大的比重。
下方範例資料有 12 個值。第一種方法會為每個完整的四值視窗產生一個結果,因此共有 9 個結果。請依每次平均要彙整的觀測值數量選擇視窗大小;變更視窗大小會同時改變平滑程度,以及有效視窗結果的數量。
使用 numpy.convolve 計算 NumPy 陣列的移動平均
numpy.convolve 會計算線性摺積。這裡的第二個陣列包含四個 1;每個摺積結果都是一個四值視窗的總和。將總和除以 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 陣列的移動平均
使用累積總和時,每個視窗的總和可透過「視窗結尾的累積總和」減去「視窗開始前的累積總和」求得。第一個視窗之前沒有累積總和可供相減,因此函式會從第一個完整視窗的位置開始切片。這會得到與 numpy.convolve 範例相同的 9 個平均值。若想了解如何利用累計總和組合出各視窗總和,這種方法很實用;再將每個總和除以視窗大小,即可換算成平均值。
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,可計算陣列的移動平均。依預設設定,只有完整視窗才會產生結果;視窗大小為 4 時,前 3 個位置尚無四值結果,第一個平均值會出現在索引 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(),計算每個完整四值視窗的平均。依預設,Series 會保留原本的長度與索引。前 3 個項目會是 NaN,因為這些位置尚未累積足夠的 4 個值;第一個計算出的平均值會對齊索引 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