04.11.4
04 変換(Box-Cox)
scipy.stats.boxcox(x) が最尤推定の \(\lambda\) と変換後の値を同時に返します。ゼロや負の値を含むデータには、同じ考え方を拡張した yeojohnson(x) を使います。このページでは Weibull 分布と対数ガンマ分布の 50,000 点で変換前後のヒストグラムを比べ、Ridge(alpha=1.0) の目的変数を yeojohnson で変換すると残差の分布がほぼ対称になることを確かめます。STL で季節性とトレンドを分解する で季節性とトレンドを取り除いた残差を先に見ておくと、このページの「残った値の分布の歪みをどう直すか」という問題設定がはっきりします。
直感 #
年収の分布を思い浮かべてください。大半の人は 300〜600 万円に集まり、少数の人が 3,000 万円や 1 億円で右に長い裾を作ります。この値をそのまま二乗誤差で回帰すると、1 億円の 1 人の誤差が 300 万円の 100 人分の誤差を上回り、モデルは少数の高額者に合わせて歪みます。対数を取ると 300 万は 14.9、1 億は 18.4 と差が縮んで分布が左右対称に近づきます。Box-Cox はこの「どれくらい強く縮めるか」を \(\lambda\) という 1 つのパラメータで調整し、データから最も正規分布に近づく \(\lambda\) を自動で選ぶ仕組みです。
1. ライブラリ #
| |
何が起きているか(ステップで見る) #
コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。
見るポイント:対数は大きな値ほど強く縮める。右に裾を引く Weibull のような分布を対称に近づける定番の変換。
式で書くと λ が何を変えているか #
Box-Cox 変換は正の \(x\) に対して
$$ x^{(\lambda)} = \begin{cases} \dfrac{x^{\lambda} - 1}{\lambda} & (\lambda \neq 0) \\\\ \log x & (\lambda = 0) \end{cases} $$で定義されます。\(\lambda = 1\) なら \(x - 1\) でほぼ何もせず、\(\lambda = 0.5\) なら平方根、\(\lambda = 0\) なら対数と、小さくするほど大きな値を強く縮めます。boxcox は変換後の値が最も正規分布に近くなる \(\lambda\) を最尤法で選びます。Yeo-Johnson は \(x \geq 0\) では \(x + 1\) に Box-Cox を、\(x < 0\) では \(1 - x\) に \(2 - \lambda\) の Box-Cox を当てて符号を戻す拡張で、ゼロをまたぐデータでも 1 つの \(\lambda\) で連続につながります。
2. 歪んだ分布を可視化 #
形状パラメータ 2.0 の Weibull 分布(正の値のみ、右に裾)と対数ガンマ分布(負の値を含み、左に裾)をそれぞれ 50,000 点生成してヒストグラムにします。
| |
裾が長い(heavy-tail)ため、そのまま二乗誤差で学習させると、裾の少数の点が損失の大部分を占めて係数が不安定になります。
3. Box-Cox 変換(正の値のみ) #
boxcox はデータがすべて正のときに使います。\(\lambda\) は最尤推定で自動的に決まり、戻り値の 2 番目に入っています。
| |
4. Yeo-Johnson 変換(負の値もOK) #
Box-Cox はゼロや負の値を扱えないため、対数ガンマのような分布には Yeo-Johnson を使います。
| |
yeojohnsonはデータを正規性に近づけるよう \(\lambda\) を推定し、ゼロをまたぐ場合も自動で扱います。
5. 変換前後で回帰の残差を比較 #
目的変数 y に歪みがあると、線形回帰の残差に偏りが生じます。Weibull 分布の y と、その平方根にノイズを足した特徴量を 1 列だけ持つデータを作り、Ridge 回帰の残差を変換前後で比べます。
| |
変換なし #
歪んだ y をそのまま目的変数にして Ridge を学習し、予測と実測の散布図と残差のヒストグラムを並べます。
| |
残差のヒストグラムが右に偏り、平均 0 の対称な分布から外れていることが分かります。
Yeo-Johnson 変換後 #
目的変数を yeojohnson で変換してから同じ Ridge を学習し、変換後の尺度で残差を比べます。
| |
残差 y_trans - pred が 0 を中心にほぼ対称になり、誤差分布が改善されました。推定 \(\lambda\) は yeojohnson(y)[1] から取得できます。Box-Cox の逆変換は scipy.special.inv_boxcox(y_trans, lmbda) で行えますが、Yeo-Johnson には scipy に逆変換関数がないため、sklearn.preprocessing.PowerTransformer(method="yeo-johnson") を使って fit_transform / inverse_transform の組で扱うのが実務では安全です。
6. 実務での指針 #
| 変換 | 適用条件 | 補足 |
|---|---|---|
| Box-Cox | 0 より大きい実数のみ | λ=0 に近いと log 変換に近づく |
| Yeo-Johnson | 負値・ゼロを含む | scikit-learn にも PowerTransformer 実装あり |
| Log | 正の値かつスケールが大きいときの手軽な選択肢 | 零点回避のため log1p を使うことも多い |
予測後に inverse_transform で元の単位に戻すロジックを忘れると、売上の予測が「2.3」のような意味のない数字で出てしまうので、変換と逆変換は必ず対にして実装します。\(\lambda\) は訓練データだけから求め、テストデータや本番では同じ値に固定します。テストデータで再推定すると、訓練と本番で尺度が変わってモデルが壊れます。変換後に StandardScaler で平均 0・分散 1 に揃えると、正則化の alpha の効き方が特徴量間で揃って学習が安定します。
7. まとめ #
目的変数が正の値だけなら boxcox、ゼロや負を含むなら yeojohnson を選び、どちらも最尤推定された \(\lambda\) を 1 つ保存しておきます。変換後は Ridge などの残差ヒストグラムを再確認し、このページの例のように右への偏りが消えて 0 を中心に対称になっているか、元の単位に逆変換した MSE や MAE が改善しているかを見ます。逆変換の実装漏れが最も多い事故なので、PowerTransformer のように transform と inverse_transform を対で持つユーティリティにまとめておくのが安全です。
- STLで季節性とトレンドを分解 — STLによる柔軟な季節・トレンド分離
- インフレ調整で実質値に揃える — CPIを考慮して時価を実質値に変換
- 差分による定常化を比較 — 原系列と差分系列で定常化効果を確認