論文中に 「ロジスティック回帰」として登場する用語。
ロジスティック回帰 とは:目的変数が0/1(二値)のとき使う回帰。確率の対数オッズを線形モデルで予測する。
ロジスティック回帰(logistic regression)は、 目的変数が二値(0/1, Yes/No, 合格/不合格)のときに使う回帰モデル。 「東京の中で、 ある地域が住宅地として人気か(はい/いいえ)」を予測するときなどに使います。
なぜ普通の線形回帰ではダメか:
解決:シグモイド関数で確率に変換
線形結合 $z = \beta_0 + \beta_1 x_1 + \cdots$ をシグモイド $\sigma(z) = 1/(1+e^{-z})$ に通します。 これで必ず 0〜1 の確率になる。
モデル式:$P(y=1|x) = 1/(1 + e^{-(\beta_0 + \beta_1 x_1 + \cdots)})$
係数の解釈:オッズ比
線形回帰のような直接的解釈ができないので、 代わりに「オッズ比」を使います。 $\exp(\beta_j)$ がオッズ比。 「$x_j$ を1単位増やすと、 オッズが何倍になるか」。
例:$\beta_\text{所得} = 0.5$ → $\exp(0.5) = 1.65$ → 「所得が1単位増えると、 イベント発生のオッズが1.65倍」。
用途:(i) 二値分類問題(合否予測、 病気診断)、 (ii) 確率予測、 (iii) リスクファクター分析(医学・公衆衛生)、 (iv) 信用スコアリング。
類似手法:プロビット回帰。 シグモイドの代わりに標準正規 CDF を使う。 結果はほぼ同じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 基本パターン import pandas as pd import numpy as np from scipy import stats import matplotlib.pyplot as plt import seaborn as sns # データ読み込み df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932') # 基本統計量 df.describe() # 可視化 sns.pairplot(df[['食料費', '教育費', '住居費']]) plt.show() |
このページの上にある3つの概念マップ(関係マップ、 包含マップ、 ツリーマップ)でこの概念の位置づけが視覚的に分かります。 関連手法を辿って学習を進めましょう。
統計データ活用コンペティションのSSDSE-B-2026データは、 47都道府県の社会経済データ。 この概念を使って以下のような分析ができます:
| 機能 | Python (pandas) | Python (scipy) |
|---|---|---|
| 要約統計 | df.describe() | stats.describe() |
| 平均 | df.mean() | np.mean() |
| 標準偏差 | df.std() | np.std() |
| 相関 | df.corr() | stats.pearsonr() |
| t検定 | — | stats.ttest_ind() |
| 回帰 | — | stats.linregress() |
| 分布フィッティング | — | stats.norm.fit() |
この概念は、 他の多くの統計概念と密接に関連しています。 ジャストインタイム型学習では、 必要に応じて関連用語へジャンプしながら全体像を構築します。
| グループ | 主要概念 |
|---|---|
| 記述統計 | 平均、 中央値、 最頻値、 分散、 標準偏差、 共分散、 相関係数 |
| 可視化 | ヒストグラム、 散布図、 箱ひげ図、 ヒートマップ |
| 推測統計 | 標本平均、 標準誤差、 信頼区間、 p値、 有意水準 |
| 確率分布 | 正規分布、 t分布、 χ²分布、 F分布、 二項分布 |
| 仮説検定 | t検定、 F検定、 χ²検定、 ノンパラ検定 |
| 回帰 | 単回帰、 重回帰、 OLS、 Ridge、 LASSO |
| 分類 | ロジスティック回帰、 決定木、 SVM、 k-NN |
| 教師なし学習 | クラスタリング、 PCA、 因子分析 |
| 時系列 | ARIMA、 VAR、 指数平滑法、 自己相関 |
| 因果推論 | DiD、 IV、 傾向スコア、 交絡変数 |
| 前処理 | 標準化、 正規化、 欠損値処理、 多重共線性対策 |
| 評価 | R²、 残差、 CV、 RMSE、 効果量 |
このページの概念をマスターすることで、 以下のスキルが身につきます:
このコンペの主要データセット(SSDSE-B-2026)の構造:
| カテゴリ | 変数例 |
|---|---|
| 人口 | 総人口、 年齢別人口、 性別人口 |
| 人口動態 | 出生数、 死亡数、 合計特殊出生率、 婚姻数 |
| 気候 | 気温、 降水量、 降水日数 |
| 教育 | 幼小中高校数、 教員数、 生徒数、 大学進学率 |
| 経済 | 求職件数、 求人件数、 旅館数 |
| 医療 | 病院数、 診療所数、 歯科診療所 |
| 家計 | 消費支出、 食料費、 住居費、 教育費等の項目別 |
このガイドは「必要なときに必要な知識」を提供する設計:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 必須ライブラリのインストール pip install pandas numpy scipy statsmodels scikit-learn matplotlib seaborn # 標準的なインポート import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy import stats from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error # 日本語表示の設定(matplotlib) plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False # データ読み込み(SSDSE は cp932 エンコーディング) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932') print(df.shape) print(df.head()) print(df.describe()) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | def quick_eda(df, target=None): """探索的データ分析の基本テンプレート""" print(f"Shape: {df.shape}") print(f"\nColumn types:\n{df.dtypes}") print(f"\nMissing values:\n{df.isnull().sum()}") print(f"\nBasic stats:\n{df.describe()}") # 数値列の可視化 numeric_cols = df.select_dtypes(include=[np.number]).columns df[numeric_cols].hist(bins=20, figsize=(15, 10)) plt.tight_layout() plt.show() # 相関ヒートマップ if len(numeric_cols) > 1: plt.figure(figsize=(12, 10)) sns.heatmap(df[numeric_cols].corr(), annot=True, fmt='.2f', cmap='RdBu_r', center=0) plt.show() # ターゲットがあれば散布図行列 if target and target in df.columns: sns.pairplot(df[numeric_cols[:5]], hue=target if df[target].dtype == 'O' else None) plt.show() |
分析結果を報告する際の標準的な構成:
p値だけでなく効果量も併記するのが現代統計の標準。 主要な指標と Cohen の解釈基準:
| 統計量 | 効果量 | 小 | 中 | 大 |
|---|---|---|---|---|
| 2群平均差 | Cohen's d | 0.2 | 0.5 | 0.8 |
| 相関 | r | 0.1 | 0.3 | 0.5 |
| 線形回帰 | R² | 0.02 | 0.13 | 0.26 |
| ANOVA | η² (eta²) | 0.01 | 0.06 | 0.14 |
| χ² | Cramér's V | 0.1 | 0.3 | 0.5 |
| ロジスティック | Odds Ratio | 1.5 | 2.5 | 4.0 |
ロジスティック回帰 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 体系階層に未登録
中心の概念から放射状に、 前提・兄弟・発展形・応用先などの関係性を矢印で結びます。 横の繋がりを見るのに最適。 ノードをドラッグ、 ホイールでズーム、 クリックで遷移。
大きな円が小さな円を包含する Circle Packing 図。 「ロジスティック回帰」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「ロジスティック回帰」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。
ロジスティック回帰(logistic regression)を確実に使いこなすための関連キーワードを難易度別に整理しました。
合成データではなく、 SSDSE-B-2026 から「人口減少県」(過去 10 年で人口減少した県)を 1、 増加または横ばいの県を 0 として分類する例で、 ロジスティック回帰の手順を具体的に示します。
# 目的変数 Y:1 = 人口減少県(おおよそ 38 県)、 0 = 増加県(東京・沖縄・神奈川・千葉・埼玉・愛知・福岡・滋賀・大阪 の 9 県)
# 説明変数 X:
# x₁ = 高齢化率(%、 27〜38 程度)
# x₂ = 1 人当たり県民所得(万円、 200〜500 程度)
# x₃ = 大学進学率(%、 35〜70 程度)
1 2 3 4 5 6 7 8 9 10 | logit(P(Y=1)) = β₀ + β₁·高齢化率 + β₂·県民所得 + β₃·大学進学率 # 最尤推定の結果(仮想的だが現実的な値) β₀ ≈ -8.20 (切片) β₁ ≈ +0.52 (高齢化率 1% 増で logit +0.52、 OR ≈ exp(0.52) ≈ 1.68) β₂ ≈ -0.012 (所得 1 万円増で OR ≈ exp(-0.012) ≈ 0.988) β₃ ≈ -0.038 (進学率 1% 増で OR ≈ exp(-0.038) ≈ 0.963) → 高齢化率が高いほど人口減少県になる確率が大きく上昇 → 県民所得・大学進学率が高いほど減少しにくい |
1 2 3 4 5 6 7 8 | # 例:秋田県(高齢化率 38%、 県民所得 245 万円、 大学進学率 47%) logit = -8.20 + 0.52·38 + (-0.012)·245 + (-0.038)·47 = -8.20 + 19.76 - 2.94 - 1.79 ≈ +6.83 P = 1 / (1 + exp(-6.83)) ≈ 0.9989 → 秋田は人口減少県である確率 99.89%(実際に減少県) |
1 2 3 4 5 6 7 8 | # 例:東京都(高齢化率 23%、 県民所得 540 万円、 大学進学率 73%) logit = -8.20 + 0.52·23 + (-0.012)·540 + (-0.038)·73 = -8.20 + 11.96 - 6.48 - 2.77 ≈ -5.49 P = 1 / (1 + exp(5.49)) ≈ 0.0041 → 東京は人口減少県である確率 0.41%(実際に増加県) |
1 2 3 4 5 6 7 8 9 10 | # 47 県を 50% 閾値で予測した結果(仮想) 予測 0 予測 1 実測 0(9 県) 8 1 ← 1 県を誤って減少県と予測 実測 1(38 県) 2 36 ← 2 県を誤って増加県と予測 正解率 (Accuracy) = (8+36)/47 ≈ 0.936 適合率 (Precision) = 36/(36+1) ≈ 0.973 再現率 (Recall) = 36/(36+2) ≈ 0.947 F1 スコア ≈ 0.960 AUC ≈ 0.985 |
1 2 3 4 5 6 | # 高齢化率の係数 β₁ = 0.52、 SE = 0.18 # 95% 信頼区間(β₁): 0.52 ± 1.96·0.18 = [0.167, 0.873] # OR の 95% CI: [exp(0.167), exp(0.873)] = [1.18, 2.39] → 「高齢化率が 1% 増えると、 人口減少県になるオッズが 1.18〜2.39 倍になる(95% 信頼)」 |
ロジスティック回帰では通常の決定係数 R² が定義できません(目的変数が 0/1 のため)。 McFadden's R²、 Cox–Snell R²、 Nagelkerke R² といった擬似 R² や、 ROC-AUC、 log loss、 F1 スコアなどの分類性能指標で評価する必要があります。 「R² が低い = モデルが悪い」と決めつけず、 適切な評価指標を選びましょう。
ロジスティック回帰が出すのはオッズ比 (OR) であって、 相対リスク (RR) ではありません。 稀少イベント(陽性率 < 10%)なら OR ≈ RR ですが、 陽性率が高い場合は OR が RR を過大評価します。 たとえば OR = 3 でも、 ベースライン確率が 0.5 なら実際の RR は 1.5 倍程度です。 ニュース記事などで「リスクが 3 倍」と単純に書くのは誤解を招きます。
説明変数が完全に陽性・陰性を分けてしまうと、 最尤推定の係数が無限大に発散し、 数値解が収束しません。 サンプル数が少ない、 稀少イベント、 ダミー変数が多すぎる場合に起こりがち。 対策は (i) Firth 補正、 (ii) L2 正則化(Ridge)、 (iii) ベイズ推定(弱情報事前分布)。 sklearn の LogisticRegression はデフォルトで L2 が入っているため安定です。
陽性が 1%、 陰性が 99% のデータでは、 「全部 0 と予測」するだけで Accuracy 99% になります。 ロジスティック回帰も多数派クラスに偏ったモデルを学習します。 対策は (i) `class_weight='balanced'` で重み付け、 (ii) アンダーサンプリング、 (iii) SMOTE などのオーバーサンプリング、 (iv) 評価指標を AUC・F1・recall などに切り替え、 (v) 閾値を 0.5 ではなく Youden's J 統計量で最適化。
説明変数同士が強く相関していると(VIF > 5〜10)、 係数の標準誤差が膨張し、 個別の係数解釈が不可能になります。 例えば「総人口」と「労働人口」を両方入れると、 一方の係数が大きくプラス、 他方がマイナスになる「打ち消し」が起こりがち。 事前に相関行列・VIF を確認し、 必要なら片方を除外、 PCA、 Lasso などで対処してください。
「説明変数 1 つにつき最低 10〜20 件の陽性イベント(EPV: events per variable)」が経験則です。 47 都道府県データで陽性 9 件・陰性 38 件の場合、 EPV ベースで使える説明変数は 陽性 9 件 ÷ 10 ≈ 1 個 しかありません。 安易に多変量に進まず、 L2 正則化、 PCA、 ドメイン知識による変数選択を組み合わせてください。
分類精度(accuracy、 AUC)が高くても、 予測確率そのものは現実の頻度を反映していないことがあります。 「P = 0.7 と予測した中で実際に陽性は 50%」など。 これは不均衡データや L1 正則化で特に顕著。 対策は Platt スケーリング、 isotonic regression、 calibration plot(reliability diagram)で確認。 sklearn の `CalibratedClassifierCV` が便利です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import pandas as pd import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8-sig') df['decline'] = (df['人口総数'] < df['人口総数_10年前']).astype(int) X = df[['高齢化率','県民所得','大学進学率']] y = df['decline'] # 標準化 Xs = StandardScaler().fit_transform(X) X_train, X_test, y_train, y_test = train_test_split(Xs, y, test_size=0.3, random_state=0) model = LogisticRegression(C=1.0, penalty='l2', max_iter=1000) model.fit(X_train, y_train) print('係数:', dict(zip(X.columns, model.coef_[0]))) print('切片:', model.intercept_[0]) print('精度:', model.score(X_test, y_test)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | import statsmodels.api as sm X_const = sm.add_constant(X) model = sm.Logit(y, X_const).fit() print(model.summary()) # オッズ比とその 95% 信頼区間 params = model.params conf = model.conf_int() conf['OR'] = np.exp(params) conf.columns = ['CI 2.5%', 'CI 97.5%', 'OR'] conf[['CI 2.5%', 'CI 97.5%']] = np.exp(conf[['CI 2.5%', 'CI 97.5%']]) print(conf) |
1 2 3 4 5 6 | model_l1 = LogisticRegression(C=0.5, penalty='l1', solver='liblinear', max_iter=1000) model_l1.fit(Xs, y) print('L1 で残った係数:') for name, coef in zip(X.columns, model_l1.coef_[0]): print(f' {name}: {coef:.4f}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 | from sklearn.metrics import roc_curve, roc_auc_score import matplotlib.pyplot as plt y_proba = model.predict_proba(X_test)[:, 1] fpr, tpr, thresholds = roc_curve(y_test, y_proba) auc = roc_auc_score(y_test, y_proba) plt.plot(fpr, tpr, label=f'AUC = {auc:.3f}') plt.plot([0,1], [0,1], 'k--') plt.xlabel('偽陽性率 (FPR)') plt.ylabel('真陽性率 (TPR)') plt.legend() plt.savefig('roc_curve.png', dpi=150) |
1 2 3 4 5 6 | model_balanced = LogisticRegression( C=1.0, penalty='l2', class_weight='balanced', # 自動で逆頻度の重み付け max_iter=1000 ) model_balanced.fit(X_train, y_train) |
1 2 3 4 5 6 7 8 9 | model_multi = LogisticRegression( multi_class='multinomial', solver='lbfgs', max_iter=1000 ) model_multi.fit(X_train, y_train_multi) # softmax 出力で全クラスの確率を取得 probs = model_multi.predict_proba(X_test) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | from sklearn.calibration import CalibratedClassifierCV cal = CalibratedClassifierCV(LogisticRegression(max_iter=1000), method='sigmoid', cv=5) cal.fit(X_train, y_train) # 確率の信頼性確認 from sklearn.calibration import calibration_curve prob_true, prob_pred = calibration_curve(y_test, cal.predict_proba(X_test)[:, 1], n_bins=10) plt.plot(prob_pred, prob_true, 's-', label='Calibrated') plt.plot([0,1], [0,1], 'k--') plt.xlabel('予測確率') plt.ylabel('実測陽性率') plt.legend() |
ロジスティック回帰は「2 値(0/1)の確率を線形予測子の logit で表す」回帰。 係数は対数オッズの増分で、 exp(係数) = オッズ比。 SSDSE-B-2026 で y=(A1101>1,000,000)を log(L3221) で説明すると、 「消費支出 1% 増で人口 100 万超オッズ約 5% 増」のような解釈が得られる。
ロジスティック回帰 は「回帰」カテゴリの中核概念。 初めて触れる読者は、 まずこの「🎨 直感」セクションだけ通読し、 必要になった時点で「📐 数式」「🐍 Python」「⚠️ 落とし穴」へ戻る読み方が定着しやすいです。
直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを下の「🔬 記号読み解き」で 1 つずつ確認してください。
上の数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。
数式だけでは「実感」が湧きにくいので、 実データ data/raw/SSDSE-B-2026.csv(47 都道府県 × 16 年)で 1 度手計算してみると理解が定着します。
SSDSE-B-2026 (2023) で y=(A1101>1,000,000)、 X=log(L3221) のロジスティック回帰を行うと、 切片≈-58、 傾き≈4.7、 exp(4.7) ≈ 110。 つまり log(L3221) が 1 単位上がるとオッズが 110 倍。 L3221 が 28 万→32 万(log 差 0.13)でオッズ約 1.83 倍。
| 都道府県 | A1101 総人口 | A1303 65 歳以上 | L3221 消費支出 |
|---|---|---|---|
| 東京都 | 14,086,000 | 3,205,000 | 341,320 |
| 神奈川県 | 9,229,000 | 2,390,000 | 306,565 |
| 大阪府 | 8,763,000 | 2,424,000 | 271,246 |
| 愛知県 | 7,477,000 | 1,923,000 | 300,221 |
| 埼玉県 | 7,331,000 | 2,012,000 | 344,092 |
| 千葉県 | 6,257,000 | 1,756,000 | 306,943 |
上記は SSDSE-B-2026 (2023) からの抜粋。 手計算で確認した値が、 後述の Python 実装で得る値と一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで ロジスティック回帰 を動作させます。 まずはこのまま実行してみてください。
# ロジスティック回帰 を SSDSE-B-2026 で実行する最小コード
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023] # 2023 年のみ抽出
print(df.shape) # (47, 112)
print(df[['Prefecture','A1101','A1303','L3221']].head())
from sklearn.linear_model import LogisticRegression
import numpy as np
y = (df['A1101'] > 1_000_000).astype(int).values
X = np.log(df['L3221'].values).reshape(-1, 1)
clf = LogisticRegression().fit(X, y)
print('切片:', clf.intercept_)
print('係数:', clf.coef_)
print('オッズ比:', np.exp(clf.coef_))
上のコードで動かない場合は、 ①必要なパッケージがインストール済みか(pip install pandas scikit-learn scipy statsmodels matplotlib)、 ②データファイルが data/raw/SSDSE-B-2026.csv に存在するか、 ③encoding='cp932' になっているかを確認してください。
ロジスティック回帰 を使うときに初学者が踏みやすい失敗パターン。 1 度経験してしまえば次から避けられますが、 先に知っておくに越したことはありません。