まとめ
- Weibull 分布や対数ガンマ分布など歪んだデータを生成し、ヒストグラムで偏りを確認します。
scipy.stats.boxcoxで正の値のみを扱う場合と、負の値を含む場合はyeojohnsonを使うケースを比較します。- 変換前後で線形回帰(Ridge)の残差を観察し、分布を整えるメリットを実感します。
1. ライブラリ #
import japanize_matplotlib
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
from scipy.stats import boxcox, yeojohnson
from sklearn.linear_model import Ridge
2. 歪んだ分布を可視化 #
plt.figure(figsize=(12, 5))
data_wb = np.random.weibull(2.0, size=50_000)
plt.hist(data_wb, bins=30, rwidth=0.9)
plt.title("Weibull 分布")
plt.show()
plt.figure(figsize=(12, 5))
data_lg = stats.loggamma.rvs(2.0, size=50_000)
plt.hist(data_lg, bins=30, rwidth=0.9)
plt.title("対数ガンマ分布")
plt.show()
裾が長い(heavy-tail)ため、そのまま学習に放り込むと損失関数が不安定になります。
3. Box-Cox 変換(正の値のみ) #
boxcox はデータが正の値のときに使います。λ(lambda)は最尤推定で自動的に決まります。
plt.figure(figsize=(12, 5))
plt.hist(boxcox(data_wb)[0], bins=30, rwidth=0.9)
plt.title("Weibull → Box-Cox")
plt.show()
4. Yeo-Johnson 変換(負の値もOK) #
Box-Cox はゼロ/負の値を扱えないため、ログガンマのような分布には Yeo-Johnson を使います。
plt.figure(figsize=(12, 5))
plt.hist(yeojohnson(data_lg)[0], bins=30, rwidth=0.9)
plt.title("log-gamma → Yeo-Johnson")
plt.show()
yeojohnsonはデータを正規性に近づけるよう λ を推定し、ゼロをまたぐ場合も自動で扱ってくれます。
5. 変換前後で回帰の残差を比較 #
目的変数 y に歪みがあると、線形回帰の残差に偏りが生じます。以下の例では Ridge 回帰を用います。
N = 1000
rng = np.random.RandomState(0)
y = np.random.weibull(2.0, size=N)
X = rng.randn(N, 5)
X[:, 0] = np.sqrt(y) + np.random.rand(N) / 10
plt.figure(figsize=(12, 5))
plt.hist(y, bins=20, rwidth=0.9)
plt.title("目的変数 y の分布")
plt.show()
変換なし #
clf = Ridge(alpha=1.0)
clf.fit(X, y)
pred = clf.predict(X)
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.title("予測 vs 実測")
plt.scatter(y, pred)
plt.plot([0, 2], [0, 2], "r")
plt.grid()
plt.subplot(122)
plt.title("残差の分布")
plt.hist(y - pred)
plt.show()
偏り(ヒストグラムの歪み)が大きいのが分かります。
Yeo-Johnson 変換後 #
y_trans, lmbda = yeojohnson(y)
clf = Ridge(alpha=1.0)
clf.fit(X, y_trans)
pred = clf.predict(X)
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.title("予測 vs 実測(変換後)")
plt.scatter(y_trans, pred)
plt.plot([0, 2], [0, 2], "r")
plt.grid()
plt.subplot(122)
plt.title("残差の分布(変換後)")
plt.hist(y_trans - pred)
plt.show()
残差がほぼ対称になり、誤差分布が改善されました。推定 λ は yeojohnson(y)[1] から取得でき、逆変換には scipy.stats.yeojohnson_normmax / yeojohnson の inv_boxcox 相当を使用します。
6. 実務での指針 #
| 変換 | 適用条件 | 補足 |
|---|---|---|
| Box-Cox | 0 より大きい実数のみ | λ=0 に近いと log 変換に近づく |
| Yeo-Johnson | 負値・ゼロを含む | scikit-learn にも PowerTransformer 実装あり |
| Log | 正の値かつスケールが大きいときの手軽な選択肢 | 零点回避のため log1p を使うことも多い |
- 予測後に逆変換するロジックを忘れずに実装する。
- λ は訓練データから求め、本番では固定して使う。
- 変換後に標準化(
StandardScaler)を挟むとより安定します。
7. まとめ #
- 目的変数が正の値のみ → Box-Cox、負値を含む → Yeo-Johnson を検討。
- 変換後はモデルの残差を再チェックし、改善がみられるかを指標(MSE / MAE)で確認。
- 逆変換の実装漏れが事故につながりやすいので、ユーティリティ関数としてまとめておくと安全です。