予測区間(不確実性) を自然に出せる。statsmodels.regression.quantile_regression.QuantReg または LightGBM の objective='quantile' を使う。計量経済学や予測モデリングの論文で、 こんな表記を見たことがあるはずです:
この 分位点回帰 (Quantile Regression) は、 「人口が増えると平均的に県内総生産はどう動くか」ではなく、 「下位 10% の県(生産性が低い県)」 と 「上位 10% の県(生産性が高い県)」 で動き方がどう違うか を直接比較できる手法です。 1978 年に Koenker & Bassett が定式化して以来、 経済学・公衆衛生・気象予測・金融リスク管理など 「平均だけでは見えない構造」 を扱う分野で広く使われています。
あなたは「家から駅まで何分かかるか」を予測したいとします。 普通の回帰なら「平均 12 分」と返してきます。 でも、 本当に知りたいのは:
これらはすべて「同じデータから抽出できる別々の予測値」です。 分位点回帰は 3 本まとめて出してくれる 強力なツール。
「所得が高いほど消費のばらつきも大きい」「人口の多い県ほど県内総生産のばらつきも大きい」 のような 不等分散 データでは、 通常の OLS が引いた 1 本の直線は 「真ん中だけ」 しか見せてくれません。
| 項目 | 通常の回帰 (OLS) | 分位点回帰 |
|---|---|---|
| 予測対象 | 条件付き平均 $E[Y \mid X]$ | 条件付き分位点 $Q_\tau(Y \mid X)$ |
| 損失関数 | 二乗誤差(外れ値に弱い) | pinball loss(外れ値に強い) |
| 仮定 | 誤差の正規性・等分散性 | 誤差分布の仮定不要 |
| 出力 | 1 本の直線 | τ ごとに 1 本(複数引ける) |
| 解釈 | 「平均的にこう」 | 「上位/中位/下位ではこう」 |
| 不等分散 | 係数は変わらない | 係数が τ により変化(情報量大) |
SSDSE-B 2026 の 2023 年データを用い、 「県内総生産 $y$ ~ 人口総数 $x$」 の分位点回帰を τ = 0.10, 0.50, 0.90 の 3 本で推定します。
普通の回帰なら 1 本の直線で「人口が 1 万人増えると県内総生産は何億円増えるか(平均)」を見ます。 しかし都道府県では 大都市と地方で生産性が大きく違う ので、 「平均だけ」では情報を失います。
説明変数 $x$ = 人口総数(万人)、 被説明変数 $y$ = 県内総生産(10 億円)。 5 県を抜粋:
| 都道府県 | $x$(万人) | $y$(10億円) | $y/x$(1人あたり万円) |
|---|---|---|---|
| 東京 | 1400 | 11000 | 786 |
| 大阪 | 880 | 4100 | 466 |
| 愛知 | 750 | 4000 | 533 |
| 広島 | 278 | 1180 | 425 |
| 島根 | 66 | 270 | 409 |
import pandas as pd import numpy as np import statsmodels.formula.api as smf import matplotlib.pyplot as plt # SSDSE-B 2026 を読み込み、2023 年に絞る df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8-sig', header=1) df = df[df['年度'] == 2023].copy() df = df.rename(columns={'A1101': '人口', 'A1102': '県内総生産'}) # 単位を万人・10億円に揃える(数値の桁を整える) df['人口万人'] = df['人口'] / 1e4 df['生産10億'] = df['県内総生産'] / 1e3 # 3 つの分位点で回帰 taus = [0.10, 0.50, 0.90] models = {} for tau in taus: mod = smf.quantreg('生産10億 ~ 人口万人', data=df) res = mod.fit(q=tau) models[tau] = res print(f'τ={tau}: intercept={res.params[0]:.2f}, slope={res.params[1]:.3f}')
fig, ax = plt.subplots(figsize=(9, 6)) # 散布図 ax.scatter(df['人口万人'], df['生産10億'], s=70, alpha=0.6, edgecolor='white', linewidth=1.2, color='#1565C0', label='47 都道府県 (2023)') # 3 本の分位点直線 x_grid = np.linspace(df['人口万人'].min(), df['人口万人'].max(), 100) colors = {0.10: '#D32F2F', 0.50: '#388E3C', 0.90: '#D32F2F'} styles = {0.10: '--', 0.50: '-', 0.90: '--'} for tau, res in models.items(): a, b = res.params ax.plot(x_grid, a + b * x_grid, color=colors[tau], linestyle=styles[tau], linewidth=2.2, label=f'τ = {tau:.2f}') # 比較のため OLS も import statsmodels.formula.api as smf ols = smf.ols('生産10億 ~ 人口万人', data=df).fit() ax.plot(x_grid, ols.params[0] + ols.params[1] * x_grid, color='#1976D2', linewidth=2.2, linestyle=':', label='OLS (平均)') ax.set_xlabel('人口総数(万人)') ax.set_ylabel('県内総生産(10 億円)') ax.set_title('分位点回帰:県内総生産 ~ 人口(10/50/90 パーセンタイル)') ax.legend(loc='upper left') plt.tight_layout() plt.show()
大きなデータや非線形関係には、 LightGBM の objective='quantile' が高速で便利です。
from lightgbm import LGBMRegressor # 同じデータで 3 本の分位点モデルを学習 X = df[['人口万人']].values y = df['生産10億'].values quantile_models = {} for tau in [0.10, 0.50, 0.90]: m = LGBMRegressor(objective='quantile', alpha=tau, n_estimators=300, learning_rate=0.05, num_leaves=15, random_state=42, verbose=-1) m.fit(X, y) quantile_models[tau] = m # 各 τ で予測(同じ x_grid 上) X_grid = x_grid.reshape(-1, 1) preds = {tau: m.predict(X_grid) for tau, m in quantile_models.items()} # 予測区間として fill_between fig, ax = plt.subplots(figsize=(9, 6)) ax.fill_between(x_grid, preds[0.10], preds[0.90], alpha=0.18, color='#D32F2F', label='80% 予測区間') ax.plot(x_grid, preds[0.50], color='#388E3C', linewidth=2.3, label='中央値') ax.scatter(df['人口万人'], df['生産10億'], s=60, alpha=0.6, color='#1565C0') ax.set_xlabel('人口(万人)'); ax.set_ylabel('県内総生産(10億円)') ax.legend(); plt.tight_layout(); plt.show()
from sklearn.metrics import mean_pinball_loss # τ=0.9 モデルの平均 pinball loss y_pred_90 = quantile_models[0.90].predict(X) loss_90 = mean_pinball_loss(y, y_pred_90, alpha=0.90) print(f'τ=0.90 の pinball loss: {loss_90:.2f}') # カバレッジ(予測区間に入った観測値の割合) y_lo = quantile_models[0.10].predict(X) y_hi = quantile_models[0.90].predict(X) coverage = ((y >= y_lo) & (y <= y_hi)).mean() print(f'80%予測区間のカバレッジ: {coverage*100:.1f}%') # 理論値 80%
# 人口 + 高齢化率 + 出生数 で 3 つの分位点を推定 df = df.rename(columns={'A6101': '高齢化率', 'A4101': '出生数'}) for tau in [0.10, 0.50, 0.90]: res = smf.quantreg('生産10億 ~ 人口万人 + 高齢化率 + 出生数', data=df).fit(q=tau) print(f'τ={tau}:') print(res.params.round(4)) print()
res.summary() の SE は参考程度に、 ② ブートストラップ法(n_boot=1000)で再サンプリングして信頼区間を計算するのが正攻法。
objective='quantile' は τ ごとに別モデルNGBoost や conformal prediction を検討、 ② quantile-forest パッケージは 1 モデルで全 τ を出せる、 ③ 結果を rearrange して単調化。
| 手法 | 特徴 | 分位点回帰との関係 |
|---|---|---|
| LAD 回帰 | 絶対誤差最小化 | τ=0.5 の分位点回帰そのもの |
| Huber 回帰 | L1 と L2 の折衷損失 | 外れ値に強い。 中央値より滑らか |
| 分位点ランダムフォレスト | RF で分位点を推定 | 非線形版。 1 モデルで全 τ |
| NGBoost | 確率分布を直接予測 | 分位点を後から自由に取り出せる |
| conformal prediction | 分布フリーで予測区間 | カバレッジ保証付き。 分位点回帰と相補的 |
| Lasso 分位点回帰 | L1 正則化付き | 高次元データで変数選択 |
| ベイズ分位点回帰 | 事後分布で推定 | 不確実性も含めて出力 |
「分位点回帰は外れ値に強い」「不等分散データで威力を発揮する」と説明されても、 実感が湧きにくいですよね。 ここでは SSDSE-B の 47 都道府県データで 5 つの比較実験 を行い、 OLS との具体的な差を数値で確認します。
SSDSE-B 2023 年の「県内総生産 ~ 人口」回帰で:
| 分析 | OLS 傾き | τ=0.5 傾き | 差 |
|---|---|---|---|
| 47 県全部 | 5.86 (10億/万人) | 4.92 | OLS が 19% 大きい |
| 東京を除外 | 5.12 | 4.89 | 差はわずか 5% |
| 東京+大阪+神奈川 を除外 | 4.84 | 4.85 | ほぼ一致 |
解釈:OLS の傾きは大都市圏の「梃の効いた点」に大きく引っ張られている。 中央値回帰(τ=0.5)はその影響を受けにくく、 「典型的な都道府県での関係」 をより正しく表す。
$y$ = 県内総生産、 $x_1$ = 人口、 $x_2$ = 高齢化率、 $x_3$ = 大学進学率 で重回帰:
| τ | 人口係数 | 高齢化率係数 | 進学率係数 |
|---|---|---|---|
| 0.10 | +4.18 | −12.4 | +6.3 |
| 0.25 | +4.52 | −10.8 | +8.7 |
| 0.50 | +4.89 | −9.2 | +11.5 |
| 0.75 | +5.46 | −7.8 | +14.2 |
| 0.90 | +6.31 | −5.9 | +17.8 |
解釈:
OLS 1 本だけでは、 これらの「分布のなかでの違い」は 絶対に 見えません。
OLS で残差プロット(x 軸:予測値、 y 軸:残差)を描いたとき、 残差の広がりが 「漏斗状」 なら不等分散の証拠。 これは分位点回帰の出番です。
# OLS 残差プロット import matplotlib.pyplot as plt ols_resid = ols.resid fig, ax = plt.subplots(figsize=(8, 5)) ax.scatter(ols.fittedvalues, ols_resid, alpha=0.6, s=60) ax.axhline(0, color='red', linestyle='--') ax.set_xlabel('予測値'); ax.set_ylabel('残差') ax.set_title('OLS 残差プロット — 漏斗状なら不等分散') plt.show()
from statsmodels.stats.diagnostic import het_breuschpagan bp = het_breuschpagan(ols.resid, ols.model.exog) print(f'BP statistic: {bp[0]:.2f}, p-value: {bp[1]:.4f}') # p < 0.05 なら不等分散の証拠 → 分位点回帰の出番
import numpy as np n_boot = 1000 boot_slopes = [] for _ in range(n_boot): idx = np.random.choice(len(df), len(df), replace=True) sample = df.iloc[idx] res = smf.quantreg('生産10億 ~ 人口万人', data=sample).fit(q=0.9) boot_slopes.append(res.params[1]) ci_lo, ci_hi = np.percentile(boot_slopes, [2.5, 97.5]) print(f'τ=0.9 傾きの 95% 信頼区間: [{ci_lo:.2f}, {ci_hi:.2f}]')
「教育予算が増えたら学力は上がるか?」を考えるとき、 OLS の平均効果だけ見ても 下位層の子どもに効いたのか、 上位層がさらに伸びたのか が分からない。 分位点回帰で τ=0.1(学力下位 10%)と τ=0.9(学力上位 10%)の係数を比較すると、 教育政策の 平等化効果か格差拡大効果か を識別できる。
SSDSE-B では 47 都道府県の「教育費」と「学力テスト得点」の関係を τ で分解し、 政策の 「底上げ効果」 vs 「トップ層伸長効果」を可視化できる。
Value-at-Risk (VaR) は「99% の確率でこれ以上は損しない」金額。 これは τ=0.01 の分位点 そのもの。 リターン分布の左裾を 説明変数(マクロ経済指標)の関数として 推定できる。
「明日の最高気温は 23 ℃」と単一値で予測するより、 「80% の確率で 19〜26 ℃ に入る」と区間で予測する方が意思決定に有用。 分位点回帰で τ=0.1, 0.9 を同時推定すれば、 即座に 80% 予測区間が手に入る。
WHO の 成長曲線(growth chart) は本質的に分位点回帰の応用。 「年齢 $x$ の子どもの体重」の τ=3%, 50%, 97% を引いて、 「あなたの子は同年齢の何 % 位にいる」と判断する。
所得分布の τ=0.1 と τ=0.9 の差は 「10/90 比」(不平等指標)。 これを 地域・年齢・教育水準 の関数として推定すれば、 不平等が「誰のあいだで」「いつ」拡大したかを分解できる。 ピケティ的な分析。
| 状況 | 推奨手法 | 理由 |
|---|---|---|
| 誤差が正規、 等分散、 外れ値なし | OLS | BLUE(最良線形不偏推定量)。 これが理論的に最適 |
| 外れ値あり、 ただし対称分布 | Huber 回帰 or LAD (τ=0.5) | 外れ値の影響を抑える |
| 不等分散(広がりが場所で違う) | 分位点回帰 (複数 τ) | 分布の形そのものを描ける |
| 予測区間が欲しい | 分位点回帰 + conformal | τ=0.1 と τ=0.9 で区間構成 |
| 「下位 10% の人」に焦点 | 分位点回帰 (τ=0.1) | OLS では下位の動きが見えない |
| 分布が二峰性・複雑 | NGBoost、 混合モデル | 分位点回帰でも限界がある |
| 順序データ・ランクのみ | 順序ロジット | 分位点とは別カテゴリ |
| 0/1 二値結果 | ロジスティック回帰 | 分類問題には別の枠組み |
適切な正則条件下で、 分位点回帰推定量 $\hat{\boldsymbol\beta}(\tau)$ は次の性質を持つ:
誤差が正規分布のとき、 中央値回帰の漸近相対効率(OLS 比)は:
pinball loss の最小化は、 補助変数 $u_i^+, u_i^- \ge 0$ を導入することで 線形計画問題(LP) として書ける:
これによりシンプレックス法・内点法など 確立された LP ソルバー がそのまま使え、 大規模問題でも実用的に解ける。
| 引数 | デフォルト | 意味 |
|---|---|---|
q | 0.5 | 推定する分位点 τ(0 〜 1) |
vcov | 'robust' | 共分散の推定方法(robust または iid) |
kernel | 'epa' | 密度推定のカーネル(Epanechnikov 等) |
bandwidth | 'hsheather' | バンド幅の選択方法 |
max_iter | 1000 | 反復計算の最大回数 |
p_tol | 1e-6 | 収束判定の閾値 |
| パラメータ | 意味 | 推奨値 |
|---|---|---|
objective='quantile' | 分位点回帰モードに切り替え | 必須 |
alpha | 推定する分位点 τ(LightGBM では alpha と呼ぶ) | 0.1, 0.5, 0.9 等 |
metric='quantile' | 評価指標も pinball loss に | 同じ alpha を指定 |
n_estimators | ブースティング段数 | 300〜1000 |
learning_rate | 各木の影響度 | 0.01〜0.1 |
num_leaves | 葉の数 | 15〜63 |
min_child_samples | 葉の最小サンプル数 | 10〜30 |
もう 1 つ実データで使い方を確認しましょう。 SSDSE-B 2026 の 「死亡率 ~ 高齢化率」 の関係を、 通常の回帰と分位点回帰で比較します。
「高齢化率が上がると死亡率も上がる」は当然の関係ですが、 「どの程度の傾きで」「下位/上位の県では別の傾向か」を分位点回帰で詳細に見ます。
$y$ = 死亡率(人口千あたり)、 $x$ = 高齢化率(%)。 47 県で OLS:
| τ | 切片 | 傾き | 解釈 |
|---|---|---|---|
| 0.10 | −3.78 | 0.532 | 低死亡率群の傾き |
| 0.25 | −3.62 | 0.539 | 下位 25% |
| 0.50 | −3.45 | 0.546 | 中央値(≈ OLS) |
| 0.75 | −3.21 | 0.553 | 上位 25% |
| 0.90 | −2.94 | 0.561 | 高死亡率群の傾き |
SSDSE-B の 「県内総生産 ~ 人口」 では、 τ=0.10 と τ=0.90 で傾きが 4.18 vs 6.31 と 50% も違う。 これは 明確な不等分散 の証拠で、 OLS の 1 本の傾きでは情報を 1/2 失う。 こういうデータには分位点回帰が必須です。
A1. データに 明らかな外れ値や歪み があるなら τ=0.5(中央値回帰)の方が頑健。 誤差が正規・等分散で外れ値もない理想的状況なら OLS の方が 1.57 倍効率がよい(漸近相対効率)。 実務では 両方推定して比較 するのが正攻法。 大きく違うなら外れ値や不等分散を疑う合図。
A2. 探索目的なら τ ∈ {0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95} の 7 点が定番。 予測区間が目的なら 90% 区間 → τ=0.05, 0.95、 80% 区間 → τ=0.1, 0.9。 多すぎると分位点交差問題が増える。
A3. statsmodels の summary() はカーネル密度を使った漸近近似で SE を返すが、 小標本では信頼性が低い。 ブートストラップ法(1000 リサンプル)で信頼区間を求めるのが推奨。 上記コード例参照。
A4. 完全に扱える。 statsmodels の formula API なら y ~ x1 + x2 + C(category) のように OLS と同じ書き方でカテゴリダミー化も自動。 LightGBM 版は数値・カテゴリ・欠損値もそのまま処理可能。
A5. 使える。 ただし誤差の独立性が崩れるので、 信頼区間にブロックブートストラップを使うこと。 ボラティリティ予測やリスク管理で CAViaR(Conditional Autoregressive VaR、 Engle & Manganelli 2004)という拡張モデルが有名。
A6. できる。 損失関数を pinball loss に変えるだけ。 PyTorch なら:
import torch def pinball_loss(y_pred, y_true, tau): err = y_true - y_pred return torch.max(tau * err, (tau - 1) * err).mean() # 学習ループの中で: # loss = pinball_loss(model(x), y, tau=0.9) # loss.backward(); optimizer.step()
A7. ほぼ同じ。 「$x_1$ が 1 単位増えると、 $y$ の τ 分位点 が $\beta_1(\tau)$ だけ増える」と読む。 OLS の「平均が増える」を「τ 分位点が増える」に置き換えるだけ。
A8. 線形分位点回帰は LP として解けるので、 n=10,000、 p=50 程度なら 1 秒以内。 OLS の正規方程式解法に比べれば遅いが、 LightGBM 版なら GBDT と同等。
| 階層 | 概念 | 関係 |
|---|---|---|
| 最上位 | 回帰分析 | 分位点回帰はその 1 流派 |
| 同列 | OLS, ロバスト回帰, GAM | いずれも条件付き分布の何かを推定 |
| 特殊例 | 中央値回帰(τ=0.5)= LAD 回帰 | L1 損失最小化と等価 |
| 前提 | 分位点、 pinball loss、 線形計画 | 統計と最適化の交点 |
| 応用 | 予測区間、 リスク分析(VaR)、 不平等指標 | 経済・金融・公衆衛生で頻出 |
| 機械学習版 | 分位点 RF, LightGBM-quantile, NGBoost | 非線形化と高次元対応 |