04.4.3
平均の変化点を可視化
series.rolling(window=14, min_periods=1).mean() の 14 日移動平均を青い太線で重ね、ax.axvline で 2 本の赤い破線を境界に引きます。読み終えると、移動平均が段差を 14 日かけてなぞる形から変化点を目で確かめ、境界の前後で平均や分散がどれだけ変わったかを言葉にして、モデルを分割するかダミー変数を足すかの判断につなげられるようになります。施策の投入日や仕様変更の日に赤い縦線を引き、14 日移動平均を重ねると、「その日を境に平均が 15 上がった」という変化を数字と図の両方で共有できます。
直感 #
値下げキャンペーンを 3 月 11 日に始め、新しい配送業者に 5 月 20 日に切り替えたとします。日次の売上の折れ線は毎日の揺れが大きく、「本当に水準が変わったか」は見た目では判断できません。そこで 14 日間の平均を取った滑らかな線を重ねると、揺れが \(1/\sqrt{14} \approx 0.27\) 倍に縮み、段差だけが残ります。その段差の位置に施策日の縦線を引けば、「線の左側は平均 80、右側は平均 95」と言い切れる図になります。ここでやっているのは ruptures のような自動検出ではなく、知っている日付に線を引いて移動平均が同意しているかを確かめる作業です。
何が起きているか(ステップで見る) #
コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。
見るポイント:どこで区切ると左右のばらつきの合計が最小になるかを総当たりで探す。
Python で描く #
次のコードは、平均 80(0〜69 日目)、95(70〜139 日目)、110(140 日目以降)の 3 区間を持つ 240 日の系列を作り、観測値を灰色、14 日移動平均を青、2 つの変化点を赤い破線で描きます。
| |
実行結果の読み方 #
灰色の観測値は 80 のまわりで揺れたあと 70 日目で 95 へ、140 日目で 110 へ階段状に跳ね上がります。青い 14 日移動平均は赤い破線の位置から約 14 日かけて次の水準に登ります。その間は斜めの坂です。これは移動平均が変化点の前後の値を混ぜて平均するためで、坂の長さが window と同じになることを確認してください。赤い破線のすぐ左と右で青い線の高さを読むと、1 本目で 80 → 95、2 本目で 95 → 110 と、どちらも 15 の差です。後半ほど灰色の線の揺れが大きいのは標準偏差を 1.5 → 2.2 に増やしたためで、変化点では平均だけでなく分散も変わりうることを示しています。
使いどころと落とし穴 #
この図は、施策や外部環境の変化を知っている人が「その日から水準が変わった」と主張する根拠として使います。境界の前後で series[:70].mean() と series[70:140].mean() のように平均を計算して差を添えると、図の印象を数字で裏づけられます。前後で平均や分散が大きく変わるなら、学習データを変化点以降に絞る、区間ダミー変数を回帰に加える、といった対処に進みます。落とし穴は、変化点を手で置くぶん「見たいところに線を引いてしまう」ことで、再現性のある分析には ruptures のような自動検出(コスト関数 "l2" で平均の変化を探す)や BOCPD を併用し、手で引いた線と検出結果が一致するかを確かめます。移動平均の window を長くしすぎると変化点が右へずれて見えるので、段差が始まる日は青い線ではなく灰色の観測値で読むようにします。
- rupturesで変化点検出 — rupturesライブラリで変化点を自動検出
- Zスコアで異常値検出 — 閾値超えの観測点を異常値として検出
- 欠損区間をハイライト — データの抜けを色付き帯で視覚的に把握