K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

06 移動平均

入門

04.11.7

06 移動平均

最終更新 2025-11-05 読了時間 4 分
まとめ
移動平均は「直近 \(n\) 点の平均を今の値の代わりに置く」だけの操作で、月ごとの細かい揺れを消して系列の大きな流れを浮かび上がらせます。このページでは米国の天然ガス消費量(月次、2013〜2021 年)を使い、rolling(12).mean() の単純移動平均(SMA)、重みを山形にした rolling(12).apply() の加重移動平均(WMA)、ewm(span=12).mean() の指数移動平均(EMA)を同じ図で比べます。rolling(12).max() と .min() で作る上下バンドから外れた月を異常候補として拾う方法も扱い、どの移動平均も pandas の 1 行で書けることを確かめます。

直感 #

毎月の天然ガス消費量は冬に跳ね上がり夏に落ちるので、1 月と 7 月を比べても「増えたか減ったか」は分かりません。直近 12 か月の平均を取ると冬と夏が 1 回ずつ含まれて季節の山谷が打ち消され、「去年 1 年間と比べて今年 1 年間はどうか」という流れだけが残ります。平均を取る窓を 12 か月から 24 か月に広げるほど線はなめらかになりますが、山や谷が実際より遅れて現れる代償があります。この「なめらかさと遅れの交換」を、重みの付け方で調整するのが WMA と EMA です。

1. ライブラリ #

1
2
3
4
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

(full_fred を利用すると FRED から最新データを取得できますが、ここではサンプルとして埋め込み済みの辞書を使用します。)


何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:窓の中の平均を窓の中央に打つだけ。窓を 12 から 24 に広げるほど滑らかだが、山や谷が遅れて現れる。

2. データを DataFrame にまとめる #

2013 年 1 月から 2021 年 12 月まで 108 か月分の天然ガス消費量(単位 BCF、10 億立方フィート)を辞書から DataFrame にし、折れ線で形を確認します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
data = {
    "value": {
        "2013-01-01": 2878.8,
        ...
        "2021-12-01": 3012.3,
    }
}

data = pd.DataFrame(data)
data.index = pd.to_datetime(data.index)
data = data.rename(columns={"value": "Natural Gas Consumption(BCF)"})

plt.figure(figsize=(12, 6))
sns.lineplot(data=data)
plt.title("天然ガス消費量(BCF)")
plt.show()

3. 単純移動平均(SMA) #

直近 12 か月の平均・最大・最小を計算し、元データと重ねます。12 にするのは、1 年分を含めて季節の山谷を打ち消すためです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
data_sma = data.copy()
rolling_obj = data_sma["Natural Gas Consumption(BCF)"].rolling(12)
data_sma["SMA(12)"] = rolling_obj.mean()
data_sma["RollingMax(12)"] = rolling_obj.max()
data_sma["RollingMin(12)"] = rolling_obj.min()

plt.figure(figsize=(12, 6))
sns.lineplot(data=data_sma[["Natural Gas Consumption(BCF)", "SMA(12)", "RollingMax(12)", "RollingMin(12)"]])
plt.title("SMA と上下バンド")
plt.show()

SMA は、季節性のある系列から滑らかなトレンドを得たいときに最初に試す方法です。RollingMax と RollingMin のバンドから外れた月をマーキングすれば、閾値を手で決めずに「過去 1 年で最も多い/少ない月」を異常候補として拾えます。


4. 加重移動平均(WMA) #

窓の中央に近い観測ほど重みを大きくしたい場合は、重み配列を定義して rolling().apply() に渡します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
weights = np.array([0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0])

def get_wma(values, w=weights):
    return np.sum(w * values) / w.sum()

data_wma = data.copy()
data_wma["WMA(12, center=True)"] = (
    data_wma["Natural Gas Consumption(BCF)"]
    .rolling(12, center=True)
    .apply(get_wma, raw=True)
)
data_wma["WMA(12, center=False)"] = (
    data_wma["Natural Gas Consumption(BCF)"]
    .rolling(12, center=False)
    .apply(get_wma, raw=True)
)

plt.figure(figsize=(12, 6))
sns.lineplot(data=data_wma)
plt.title("加重移動平均(center True/False)")
plt.show()

center=True にすると窓の中央に値が置かれるので、季節の山谷の位置を元データとずらさずにトレンドを見たいときに向きます。


5. 指数移動平均(EMA) #

古い観測の重みを \(1-\alpha\) 倍ずつ指数的に減らす方法で、EMA(平滑化指数)とも呼ばれます。span=12 が「12 か月の SMA と同程度のなめらかさ」に相当し、adjust=False にすると教科書どおりの再帰式 \(\mathrm{EMA}t = \alpha y_t + (1 - \alpha)\mathrm{EMA}{t-1}\) に一致します。

1
2
3
4
5
6
7
8
9
data_ema = data.copy()
data_ema["EMA(12)"] = (
    data_ema["Natural Gas Consumption(BCF)"].ewm(span=12, adjust=False).mean()
)

plt.figure(figsize=(12, 6))
sns.lineplot(data=data_ema)
plt.title("指数移動平均 (EMA)")
plt.show()

EMA は最新値への追従が速いので、オンライン予測や在庫管理のように「今月の値をすぐ反映したい」リアルタイムの分析で重宝します。


6. まとめ #

種類特徴実装
SMA全観測を均等に平均Series.rolling().mean()
WMA重みをカスタム、窓の中心をずらせるrolling().apply()
EMA指数減衰、最新値に敏感Series.ewm().mean()

3 種類はいずれも「窓の中の重みつき平均」で、違いは重みの形と結果を書く位置だけです。季節の山谷を消してトレンドを見るならまず rolling(12).mean() の SMA、最新月の変化をすぐ反映したいなら ewm(span=12) の EMA、過去データで季節サイクルの中心を正確に取りたいなら rolling(12, center=True).apply() の WMA と覚えておくと使い分けが楽になります。SMA の rolling().max() と .min() で作る上下バンドは、閾値を手で決めずに異常月を拾う道具としてそのまま使えます。