本ページでは、 分布の形状指標を統合的に解説します。 歪度(skewness)・尖度(kurtosis)・モーメント・正規性検定を一気通貫で扱います。
平均・分散だけでは分布の「形」は分かりません。 左右非対称か、 裾が厚いか、 を測る指標を体系的に学びます。
論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:
確率変数 $X$ のk 次のモーメントとk 次の中心モーメント:
$$m_k = \mathbb{E}[X^k], \quad \mu_k = \mathbb{E}[(X-\mu)^k]$$
記号読み:$m_k$ は「エム・サブ・ケー」原点周りのモーメント、 $\mu_k$ は「ミュー・サブ・ケー」中心モーメント、 $\mu = m_1$ は平均。 $\mathbb{E}[\cdot]$ は期待値(確率重み付き平均)、 $(X-\mu)^k$ は中心からのズレを $k$ 乗したもの → 「散らばり」「左右非対称」「裾の重さ」などを順番に取り出す装置。
🌐 関連手法・派生:モーメントは 分散・標準偏差・正規分布のパラメータ推定(モーメント法)と直結します。 高次モーメントは分布形状判定の核です。
$$\gamma_1 = \mathbb{E}\left[\left(\frac{X-\mu}{\sigma}\right)^3\right] = \frac{\mu_3}{\sigma^3}$$
記号読み:$\gamma_1$ は「ガンマ・サブ・1」歪度。 標本では Fisher–Pearson 標準化 $g_1 = \sum (x_i-\bar{x})^3 / (n\sigma^3)$。
| 歪度 | 形状 | 代表的分布 |
|---|---|---|
| $\gamma_1 > 0$(右歪み) | 右の裾が長い、 最頻 < 中央 < 平均 | 所得、 待ち時間、 対数正規 |
| $\gamma_1 = 0$ | 対称 | 正規、 一様、 t分布 |
| $\gamma_1 < 0$(左歪み) | 左の裾が長い、 平均 < 中央 < 最頻 | 試験得点(天井効果) |
データ [1, 2, 2, 3, 10]:平均 3.6、 σ ≈ 3.36
1 2 3 4 5 | import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) for col in ['一人当たり県民所得', '人口密度', '高齢化率', '持ち家比率']: print(f'{col}: skew = {df[col].skew():.3f}') |
$$\beta_2 = \mathbb{E}\left[\left(\frac{X-\mu}{\sigma}\right)^4\right] = \frac{\mu_4}{\sigma^4}$$
記号読み:$\beta_2$ は生尖度。 正規分布で 3 になる。
$$\gamma_2 = \beta_2 - 3$$
正規分布の尖度 3 を基準に「ゼロ中心」に修正。 pandas / scipy はこちらを返す。
| 超過尖度 | 名前 | 形状 |
|---|---|---|
| $\gamma_2 > 0$ | leptokurtic(尖り) | 頂が尖り、 裾が厚い(外れ値多) |
| $\gamma_2 = 0$ | mesokurtic | 正規と同じ尖り |
| $\gamma_2 < 0$ | platykurtic(平ら) | 頂が平らで裾が薄い |
1 2 | for col in ['一人当たり県民所得', '人口密度', '高齢化率', '持ち家比率']: print(f'{col}: kurtosis = {df[col].kurtosis():.3f}') |
1 2 3 4 5 | from scipy import stats x = df['一人当たり県民所得'] print('Shapiro:', stats.shapiro(x)) print('Jarque-Bera:', stats.jarque_bera(x)) print('D\'Agostino:', stats.normaltest(x)) |
標本分位点 vs 理論分位点。 一直線なら正規。 目視で正規性を確認する最強の方法。
1 2 3 4 | import statsmodels.api as sm import matplotlib.pyplot as plt sm.qqplot(x, line='s') plt.show() |
$$JB = \frac{n}{6}\left(\gamma_1^2 + \frac{\gamma_2^2}{4}\right)$$
正規分布のもと $\chi^2_2$ に従う。
右歪みデータ(所得、 価格、 待ち時間など)に有効。 $y = \log(x+1)$ で 0 も扱える。
1 2 3 4 | import numpy as np df['log_所得'] = np.log1p(df['一人当たり県民所得']) print('変換前 skew:', df['一人当たり県民所得'].skew()) print('変換後 skew:', df['log_所得'].skew()) |
$$y(\lambda) = \begin{cases} (x^\lambda - 1)/\lambda & (\lambda \ne 0) \\ \log x & (\lambda = 0)\end{cases}$$
$\lambda$ を最尤推定して最も正規に近づける。 $x > 0$ が必要。
Box-Cox を負の値にも拡張した変換。
1 2 3 4 | from sklearn.preprocessing import PowerTransformer pt = PowerTransformer(method='yeo-johnson') df[['所得_yj']] = pt.fit_transform(df[['一人当たり県民所得']]) print(df['所得_yj'].skew()) |
| 分布 | 歪度 | 超過尖度 |
|---|---|---|
| 正規 $N(\mu, \sigma^2)$ | 0 | 0 |
| 一様 | 0 | -1.2 |
| t分布 (df=5) | 0 | +6 |
| 指数 | +2 | +6 |
| 対数正規 (σ=1) | +6.18 | +110 |
| ベルヌーイ (p=0.5) | 0 | -2 |
| 落とし穴 | 対処 |
|---|---|
| 尖度の定義違い | 「超過尖度」か「生尖度」を明示。 pandas/scipy は超過尖度。 |
| 小標本の歪度・尖度 | n < 20 では推定が不安定。 ブートストラップ CI を付ける。 |
| 外れ値の影響 | 高次モーメントは外れ値に超敏感。 ロバスト指標(MAD等)も併用。 |
| 「p > 0.05 で正規」と決定 | 「棄却できない」は「正規である」ではない。 QQ プロット併用。 |
| 大標本での過検出 | n が大きいと僅かな逸脱でも有意に。 効果量を見る。 |
| 対数変換時の 0 値 | log1p(x) または log(x + c)。 |
| Box-Cox を負値に適用 | Yeo-Johnson に切り替える。 |
1 2 3 4 5 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) num = df.select_dtypes(include='number') sk = num.skew().sort_values(ascending=False) print(sk.head(10)) |
1 2 3 4 5 6 7 8 9 | import numpy as np import matplotlib.pyplot as plt import statsmodels.api as sm fig, axes = plt.subplots(2, 2, figsize=(12, 8)) df['人口密度'].hist(ax=axes[0,0], bins=20); axes[0,0].set_title(f'生 skew={df["人口密度"].skew():.2f}') np.log1p(df['人口密度']).hist(ax=axes[0,1], bins=20); axes[0,1].set_title(f'log skew={np.log1p(df["人口密度"]).skew():.2f}') sm.qqplot(df['人口密度'], line='s', ax=axes[1,0]) sm.qqplot(np.log1p(df['人口密度']), line='s', ax=axes[1,1]) plt.tight_layout(); plt.show() |
1 2 3 4 5 | from scipy import stats for col in num.columns[:10]: sw = stats.shapiro(num[col]).pvalue jb = stats.jarque_bera(num[col]).pvalue print(f'{col}: SW p={sw:.4f}, JB p={jb:.4f}') |
「人口密度は歪んでいたので log を取りました。」
「人口密度は強い右歪み(歪度 = 3.45, 超過尖度 = 13.2、 Shapiro–Wilk p < .001、 QQ プロットも著しく逸脱)を示したため、 log1p 変換を適用。 変換後は歪度 0.18、 超過尖度 -0.42、 QQ プロットでもおおむね一直線に。 以降の解析は変換後の値で実施。」
| 用途 | 関数 |
|---|---|
| 歪度 | pandas .skew(), scipy.stats.skew |
| 尖度(超過) | pandas .kurtosis(), scipy.stats.kurtosis |
| Shapiro–Wilk | scipy.stats.shapiro |
| Jarque–Bera | scipy.stats.jarque_bera |
| D'Agostino | scipy.stats.normaltest |
| K-S | scipy.stats.kstest |
| Anderson–Darling | scipy.stats.anderson |
| QQプロット | statsmodels.api.qqplot, scipy.stats.probplot |
| Box-Cox | scipy.stats.boxcox |
| Yeo-Johnson | sklearn.preprocessing.PowerTransformer |
金融工学では「co-skewness」「co-kurtosis」など多変量への拡張が研究されています。 ただし高次モーメントは標本誤差が大きく、 実務では 3 次・4 次までが標準。
順序統計量に基づくモーメント。 通常のモーメントより外れ値に頑健。 水文学・気象統計でよく使われる。
$$L_1 = E[X_{1:1}], \quad L_2 = (E[X_{2:2}] - E[X_{1:2}])/2, \dots$$
L-skewness $\tau_3 = L_3/L_2$、 L-kurtosis $\tau_4 = L_4/L_2$ は有界。
$$M_X(t) = \mathbb{E}[e^{tX}]$$
テイラー展開すると:
$$M_X(t) = \sum_{k=0}^{\infty} \frac{m_k}{k!} t^k$$
つまりすべてのモーメントを内包する関数。 分布の同一性・独立和の証明等に多用される。
$\phi_X(t) = \mathbb{E}[e^{itX}]$。 MGF が存在しない分布でも常に存在し、 分布を一意に決める。
$\gamma_1 = 0,\ \gamma_2 = 0$(基準)
$\gamma_1 = 0,\ \gamma_2 = -6/5 = -1.2$(平坦)
$\gamma_1 = 2,\ \gamma_2 = 6$(強い右歪み・厚裾)
$\gamma_1 = \sqrt{8/k},\ \gamma_2 = 12/k$(k が小さいほど強い歪み)
$\gamma_1 = 2/\sqrt{\alpha},\ \gamma_2 = 6/\alpha$
$\alpha > \beta$ で左歪み、 $\alpha < \beta$ で右歪み。 $\alpha=\beta$ で対称。
$\gamma_1 = (e^{\sigma^2}+2)\sqrt{e^{\sigma^2}-1}$。 $\sigma$ が小さくても顕著な右歪み。
$\gamma_1 = 0,\ \gamma_2 = 6/(\nu-4)$(裾が厚い)
本講座は実データのみを扱いますが、 教育目的では「ある分布から大量サンプリングして歪度・尖度の収束を見る」シミュレーションが有効。 本ページでは数式と SSDSE-B の実データ計算で代替します。
SSDSE-B の主要変数の歪度・尖度を計算し、 上記の「主要分布」表と照合して、 どの分布に近いかを判断する練習を Q1-Q3 で実施できます。
A. 必要条件にはなりますが十分ではありません。 正規分布は $\gamma_1=0, \gamma_2=0$ ですが、 他にも条件を満たす分布があります。 QQプロット併用が必須。
A. 分散は「ばらつき」を測りますが「対称性」や「裾の厚さ」は捕捉しません。 リスク管理・モデル選択では裾の挙動が決定的に重要。
A. 一般に n < 30 では推定が不安定。 ブートストラップ CI で評価するか、 中央値ベースの頑健な歪度(Bowley)を使う。
歪度・尖度の値から「どの分布族に近いか」を推測する目安:
| 歪度 | 超過尖度 | 候補分布 | 対処 |
|---|---|---|---|
| ≈ 0 | ≈ 0 | 正規 | そのまま解析可 |
| ≈ 0 | 負 | 一様・ベータ(2,2) | そのまま or ベータフィット |
| ≈ 0 | 正 | t分布・混合正規 | 頑健統計、 ノンパラ |
| 1〜2 | 3〜6 | 対数正規・ガンマ | log 変換 |
| > 2 | > 6 | パレート・指数 | log 変換、 Box-Cox |
| 負 | 正 | 天井効果データ | 反転後 log、 順位ベース |
分布のパラメータを「標本モーメント=理論モーメント」で推定する古典的手法。
MLE より計算簡便だが、 一般に効率が悪い(漸近分散が大きい)。
1 2 3 4 5 6 7 | import seaborn as sns import matplotlib.pyplot as plt fig, axes = plt.subplots(1, 3, figsize=(15, 4)) sns.histplot(df['人口密度'], kde=True, ax=axes[0]) sns.boxplot(y=df['人口密度'], ax=axes[1]) sns.violinplot(y=df['人口密度'], ax=axes[2]) plt.tight_layout(); plt.show() |
尺度と分布の形 関連の補強キーワード。 クリックで該当箇所へ:
SSDSE-B の連続変数(所得・人口密度・持ち家比率)について、 尺度水準と分布形状(歪度・尖度・正規性)を体系的に分析する完全例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import pandas as pd import numpy as np import matplotlib.pyplot as plt from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) # 各変数の尺度・形状指標 fig, axes = plt.subplots(2, 3, figsize=(14, 8)) vars_to_check = ['一人当たり県民所得','人口密度','持ち家比率','世帯人員','高齢化率','就業率'] results = [] for i, var in enumerate(vars_to_check): x = df[var].dropna().values skew = stats.skew(x) kurt = stats.kurtosis(x) # excess kurtosis shapiro_w, shapiro_p = stats.shapiro(x) results.append({ '変数': var, '平均': x.mean(), 'SD': x.std(), '歪度': skew, '尖度': kurt, 'Shapiro-W': shapiro_w, 'Shapiro-p': shapiro_p, }) ax = axes[i // 3, i % 3] ax.hist(x, bins=15, edgecolor='black', alpha=0.7) ax.set_title(f'{var}\n歪度={skew:.2f}, 尖度={kurt:.2f}') plt.tight_layout(); plt.savefig('shape_check.png', dpi=110) print(pd.DataFrame(results).round(3)) |
| 項目 | 値 | 参考 | 解釈 | |
|---|---|---|---|---|
| 変数 | 歪度 | 尖度 | Shapiro-p | 解釈 |
| 一人当たり県民所得 | +2.31 | +5.84 | < 0.001 | 右に長い裾(東京が突出) |
| 人口密度 | +3.45 | +12.5 | < 0.001 | 極端な右裾(log 変換推奨) |
| 持ち家比率 | -0.42 | -0.18 | 0.21 | ほぼ対称 |
| 世帯人員 | +0.12 | -0.31 | 0.45 | 正規に近い |
| 高齢化率 | +0.05 | -0.42 | 0.62 | 正規に近い |
| 就業率 | -0.18 | -0.51 | 0.38 | ほぼ対称 |
👉 値は SSDSE-B-2026 の典型値。 同じ手順で他都道府県・他変数にも適用可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from sklearn.preprocessing import PowerTransformer, QuantileTransformer, StandardScaler import matplotlib.pyplot as plt # 右に裾の長い変数の変換比較 x = df['人口密度'].values.reshape(-1, 1) fig, axes = plt.subplots(1, 4, figsize=(16, 4)) for i, (name, tr) in enumerate([ ('元データ', None), ('Standard', StandardScaler()), ('Yeo-Johnson', PowerTransformer(method='yeo-johnson')), ('Quantile→Normal', QuantileTransformer(output_distribution='normal')), ]): if tr is None: z = x.ravel() else: z = tr.fit_transform(x).ravel() axes[i].hist(z, bins=15, edgecolor='black') axes[i].set_title(f'{name}\n歪度={stats.skew(z):.2f}') plt.tight_layout(); plt.savefig('transform_compare.png', dpi=110) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from scipy import stats import numpy as np # Box-Cox 変換(正値のみ) x = df['一人当たり県民所得'].values # 全部正値 x_bc, lambda_bc = stats.boxcox(x) print(f'Best Box-Cox λ = {lambda_bc:.3f}') print(f'変換前 歪度 = {stats.skew(x):+.3f}') print(f'変換後 歪度 = {stats.skew(x_bc):+.3f}') # Yeo-Johnson(負値も OK) x_yj, lambda_yj = stats.yeojohnson(x) print(f'Best Yeo-Johnson λ = {lambda_yj:.3f}') # Anderson-Darling 検定(より厳しい正規性検定) result = stats.anderson(x_bc, dist='norm') print(f'AD stat = {result.statistic:.3f}') for sig, crit in zip(result.significance_level, result.critical_values): print(f' α={sig}%: critical={crit:.3f}, 棄却={"是" if result.statistic > crit else "否"}') # Jarque-Bera(歪度・尖度ベース) jb_stat, jb_p = stats.jarque_bera(x) print(f'\nJarque-Bera = {jb_stat:.2f}, p = {jb_p:.4f}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import optuna from scipy import stats # 最適な変換を Optuna で探索(正規性最大化) def objective(trial): method = trial.suggest_categorical('method', ['none', 'log', 'sqrt', 'boxcox', 'yeo-johnson']) x = df['人口密度'].values if method == 'none': z = x.copy() elif method == 'log': z = np.log(x + 1) elif method == 'sqrt': z = np.sqrt(x) elif method == 'boxcox': z, _ = stats.boxcox(x) else: z, _ = stats.yeojohnson(x) # 歪度の絶対値を最小化(より正規に近づける) return abs(stats.skew(z)) study = optuna.create_study(direction='minimize') study.optimize(objective, n_trials=20) print('Best transform:', study.best_params, '|skew|:', study.best_value) |
| ライブラリ / 関数 | 用途 |
|---|---|
scipy.stats.skew, kurtosis | 歪度・尖度 |
scipy.stats.shapiro, kstest, jarque_bera | 正規性検定 |
scipy.stats.probplot | Q-Q プロット |
scipy.stats.boxcox, yeojohnson | 分布変換 |
statsmodels.graphics.gofplots.qqplot | Q-Q(より柔軟) |