論文一覧に戻る
📚 用語解説(ジャストインタイム型データサイエンス教育)
決定木
Decision Tree
「もしXがY以上なら...」のような条件分岐で予測するモデル。可読性高いが過学習しやすい。
機械学習決定木decision tree
🔖 キーワード索引
「decision tree」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「decision tree」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
decision tree統計分析SSDSE-B-2026前提条件適用範囲落とし穴関連手法Python 実装検証方法
これらのキーワードは「decision tree の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🔖 キーワード索引
💡 30秒で分かる結論
🍰 まずはやさしく
はいかいいえで分ける樹のような仕組みです。
データを分けて答えを予測するために使います。
スマホのプラン選びのような条件分けです。
この章では決定木の基本について読みます。
- 定義:「もしXがY以上なら...」のような条件分岐で予測するモデル。可読性高いが過学習しやすい。
- カテゴリ:機械学習
- 分岐基準:分類はジニ不純度・情報利得、 回帰は分散減少(MSE)。
- 長所:解釈性・前処理不要・非線形可・カテゴリ自動対応。
- 短所:高分散・外挿不可・1 本では精度限界 → アンサンブル化。
📖 包括的解説 — この概念を完全マスター
📍 学習の3ステップ
- 定義を理解する:この概念は何か? 数式や条件を確認
- 具体例を見る:実データ(SSDSE 等)で計算してみる
- 応用する:自分のデータに適用、 結果を解釈
🔧 Python実装パターン
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A4101(出生数) A4103(合計特殊出生率) L3221(消費支出(二人以上の世帯))
北海道 24,430 1.06 296,888
東京都 86,348 0.99 341,320
沖縄県 12,549 1.6 251,222
…(全 47 行)
1
2
3
4
5
6
7
8
9
10
11
12
13 | # 基本パターン: SSDSE-B-2026 を読み込み、決定木で使う特徴量を確認
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 1行目=英字コード, 2行目=日本語名 → コード行を列名にするため skiprows=[1]
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d23 = df[df['SSDSE-B-2026'] == 2023] # 直近年度(2023)に絞る
# A4101:出生数, L3221:消費支出, A4103:合計特殊出生率 の関係を可視化
sns.pairplot(d23[['A4101', 'L3221', 'A4103']])
plt.show()
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 このブロックは標準出力には何も出さない
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
📚 統計概念マップでの位置
このページの上にある3つの概念マップ(関係マップ、 包含マップ、 ツリーマップ)でこの概念の位置づけが視覚的に分かります。 関連手法を辿って学習を進めましょう。
🎯 SSDSE-B-2026 で挑戦
統計データ活用コンペティションの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() |
🚧 一般的な落とし穴と対策
- 外れ値の影響:散布図・ 箱ひげ図で確認、 ロバスト手法も検討
- サンプルサイズ不足:power analysis で事前に確認
- 仮定の違反:正規性、 独立性、 等分散性をチェック
- 多重比較問題:補正(Bonferroni、 FDR)を適用
- p-hacking:事前登録(pre-registration)で防ぐ
- 因果と相関の混同:観察データから因果結論を出さない
📊 結果報告の標準フォーマット
- 点推定:得られた値
- 不確実性:信頼区間または標準誤差
- サンプルサイズ:n を明記
- 効果量:実質的な意義
- p値:統計的有意性
- 仮定の確認:診断プロット
🌐 関連分野での応用
- マーケティング:A/Bテスト、 顧客分析
- 医療:臨床試験、 疫学研究
- 金融:リスク管理、 ポートフォリオ
- 製造:品質管理、 工程最適化
- 公共政策:効果評価、 計画立案
- 研究:仮説検証、 探索的解析
🎓 さらに学ぶための文献
- Wasserman "All of Statistics"
- Hastie, Tibshirani & Friedman "The Elements of Statistical Learning"
- Gelman & Hill "Data Analysis Using Regression"
- VanderPlas "Python Data Science Handbook"
🔗 統計用語ネットワーク
この概念は、 他の多くの統計概念と密接に関連しています。 ジャストインタイム型学習では、 必要に応じて関連用語へジャンプしながら全体像を構築します。
主要な関連概念のグループ
| グループ |
主要概念 |
| 記述統計 | 平均、 中央値、 最頻値、 分散、 標準偏差、 共分散、 相関係数 |
| 可視化 | ヒストグラム、 散布図、 箱ひげ図、 ヒートマップ |
| 推測統計 | 標本平均、 標準誤差、 信頼区間、 p値、 有意水準 |
| 確率分布 | 正規分布、 t分布、 χ²分布、 F分布、 二項分布 |
| 仮説検定 | t検定、 F検定、 χ²検定、 ノンパラ検定 |
| 回帰 | 単回帰、 重回帰、 OLS、 Ridge、 LASSO |
| 分類 | ロジスティック回帰、 決定木、 SVM、 k-NN |
| 教師なし学習 | クラスタリング、 PCA、 因子分析 |
| 時系列 | ARIMA、 VAR、 指数平滑法、 自己相関 |
| 因果推論 | DiD、 IV、 傾向スコア、 交絡変数 |
| 前処理 | 標準化、 正規化、 欠損値処理、 多重共線性対策 |
| 評価 | R²、 残差、 CV、 RMSE、 効果量 |
学習順序の推奨
- 記述統計(平均、 分散、 標準偏差)
- 可視化(ヒストグラム、 散布図)
- 確率分布(正規分布)
- 推測統計(標準誤差、 信頼区間、 p値)
- 仮説検定(t検定、 χ²検定)
- 相関と回帰(単回帰、 重回帰)
- 多変量解析(PCA、 クラスタリング)
- 機械学習(決定木、 RF、 NN)
- 時系列・因果推論(応用)
📝 実践練習 — SSDSE-B-2026 で挑戦
初級課題
- 東北6県の家計食料費の基本統計量を計算
- 食料費のヒストグラムを描く
- 食料費と教育費の散布図を描く
- 都道府県を「東日本/西日本」に分け、 平均を比較
中級課題
- 家計支出 5項目で相関行列を作成、 ヒートマップ可視化
- 食料費 → 教育費の単回帰を実行、 残差分析
- 家計5項目で PCA を実施、 バイプロット表示
- k-means (k=3) で都道府県をクラスタリング、 解釈
上級課題
- 地域別の家計パターンに有意差があるか ANOVA で検定
- 重回帰で教育費を予測、 多重共線性を VIF で確認
- Ridge/LASSO で正則化、 CV で α を最適化
- 階層クラスタリングと Ward 法で都道府県を分類、 デンドログラム作成
📍 あなたが今見ているもの
🍰 まずはやさしく
条件を繰り返して答えを出すモデルです。
どのデータが重要かを見つけるために使います。
地域の人口でグループを分けるような例です。
ここでは具体的なデータの分け方を読みます。
論文中に 「決定木」として登場する用語。
決定木 とは:「もしXがY以上なら...」のような条件分岐で予測するモデル。可読性高いが過学習しやすい。
本ページでは SSDSE-B-2026 の総人口 (A1101) が 200 万人以上か否かを目的変数に、 年平均気温 (B4101)・消費支出 (L3221)・合計特殊出生率 (A4103) を特徴量として深さ 3 の決定木を訓練し、 分岐条件と葉ノード値・特徴重要度・テキスト/グラフでの可視化を実演する。
「decision tree」は 特徴量の閾値による再帰的分割で予測する非線形モデルで、 各内部ノードは「X < t」の二分質問、 葉ノードは予測値 (回帰なら平均、 分類ならクラス) を保持する。 分割基準は不純度の減少 (Gini / エントロピー / MSE)、 過学習対策に剪定 (pruning) と深さ制限がある。 本ページでは ID3 / CART / RandomForest / GBDT への発展を扱う。
🎨 直感で掴む — 決定木 の本質
🍰 まずはやさしく
質問を繰り返して絞り込むゲームのようなものです。
ルールを分かりやすく見える化するために使います。
部活のメンバー選びのような条件分けです。
ここでは仕組みを直感的に理解して読みます。
決定木は「はい/いいえの質問を繰り返してデータを分けていく」モデル。 SSDSE-B-2026 で合計特殊出生率(A4103)を予測するとき、 「総人口 > 500 万?」「いいえ → 65歳以上人口比率 > 30%?」のように分岐し、 最終的に各葉ノードで「出生率の平均」を予測する。 ルールが可視化できるのが最大の利点。
💡 ポイント:決定木 を初めて学ぶときは「正確な定義」より「どんな問題を解くための道具か」を先に押さえてください。 数式は次の「📐 数式」セクションで丁寧に展開します。
📌 比喩がうまく刺さらないときは、 自分の身近な例(家計簿・スポーツの記録・成績表)に置き換えてみると理解が定着します。 SSDSE-B-2026 を電卓代わりに触りながら、 上の説明を再読すると効果的です。
🎨 概念図で押さえる
決定木は 「特徴空間を軸平行な矩形へ再帰分割し、 各葉に多数決クラスまたは平均値を割り当てる」。 以下 3 つの図で「分割の幾何」「Gini と情報量」「深さと過学習」を視覚化する。
図 1: 軸平行分割が作る矩形領域
→ 決定木の分割は 常に座標軸に平行。 斜め境界が必要な問題は深い木になりがちで、 そこが SVM やロジ回との大きな違い。
図 2: Gini 不純度の分割評価
→ 分割は 「子の重み付き Gini を最小化」。 これが情報利得最大化と同義。 全特徴・全閾値を貪欲探索する。
図 3: 深さと過学習
→ 深さ 1 増えるごとに葉が倍。 訓練誤差は単調減少だが、 汎化誤差は最適深さ後に再び上昇。 max_depth や ccp_alpha で正則化する。
🎨 概念図で押さえる(決定木の全体像)
図 R240-1: 決定木の構造。 ルートから条件分岐を辿り、 葉ノードで予測値(クラスまたは数値)が決定される。
図 R240-2: 決定木は座標軸に平行な分割で非線形な決定境界を構築できる。 ロジスティック回帰など線形モデルとの差異を視覚的に把握できる。
図 R240-3: 正則化と過学習の概念図。 決定木では深さ・葉数を制御する正則化(max_depth, ccp_alpha)で過学習を防ぐ。
✅ 理解度チェック — 決定木の核心を自問する 8 問
SSDSE-B-2026 (47 都道府県データ) を題材に、 決定木の本質を自問する 8 問。 各問は 分岐基準・剪定・過学習・解釈性・アンサンブル という 5 つの核心概念から構成されている。 答えを隠して 60 秒ほど考えてから読み進めてほしい。
Q1. SSDSE-B-2026 の「消費支出 (L3221)」を目的変数に決定木回帰を構築するとき、 分岐基準は何を使うべきか?
A1. 分散減少 (MSE 削減) を使う。 各分割候補に対し $\Delta = \mathrm{Var}(\text{親}) - \frac{n_L}{n}\mathrm{Var}(\text{左}) - \frac{n_R}{n}\mathrm{Var}(\text{右})$ を最大化する分割を採用する。 分類問題のジニ・エントロピーは連続値目的変数では使えない。
Q2. 葉ノード数が 47 (=都道府県数) になるまで分岐させた決定木の問題点は?
A2. 訓練データ上は完全に再現できるが、 各葉が 1 サンプルのみで 純粋な過学習。 新しい都道府県データ (もし沖縄が分裂したら?など) には全く対応できない。 max_depth・min_samples_leaf・ccp_alpha による剪定が必須。
Q3. 「年平均気温 (B4101) が高いと出生率 (A4103) 高」というルールが上位に来た。 これは因果関係か?
A3. 相関であって因果ではない。 決定木は予測パターンを学習するだけで、 交絡変数 (例: 地方部であることが両者を引き起こす) を考慮しない。 因果推論には DAG・操作変数・回帰不連続といった別枠組みが必要。
Q4. CART・ID3・C4.5 の違いを一行で説明せよ。
A4. ID3 = 情報利得・離散値のみ、 C4.5 = 情報利得比・連続値対応・剪定あり、 CART = ジニ不純度・二分木・分類と回帰両対応。 scikit-learn の DecisionTreeClassifier は CART を実装している。
Q5. ランダムフォレストとは何が違うか?
A5. RF は決定木のアンサンブル。 ブートストラップサンプリング + 特徴量サブセットで 分散を下げる。 単体の決定木は高分散だが、 RF は数百本の平均で安定する。 ただし解釈性は犠牲になる (1 本の木の可視化はできなくなる)。
Q6. ccp_alpha パラメータの役割は?
A6. Cost-Complexity Pruning の正則化係数。 大きくすると葉数が減り、 単純な木になる。 0 では剪定なし、 ∞ では根ノードのみ。 交差検証で最適値を探すのが標準手順。
Q7. 決定木が「外挿不可」と言われる理由は?
A7. 葉ノードの予測値は訓練データの平均値であり、 訓練データ外の領域 (例: 人口 5000 万の県) は最も近い葉の値を返すだけ。 線形回帰なら直線を伸ばして外挿できるが、 木は階段関数なので定義域外の値は予測できない。
Q8. 解釈性を保ちつつ精度を上げるには?
A8. (1) 深さを制限した浅い木で SHAP 値 を併用、 (2) 勾配ブースティング + SHAP で特徴量寄与を可視化、 (3) Rule Fit で重要ルールだけ抽出、 (4) EBM (Explainable Boosting Machine) を使う ── など。 ブラックボックス化を避けつつ高精度を狙う実務的選択肢。
🎮 触って理解する — 決定木の決定境界プレイグラウンド
ここはマウス/タッチで直接触れる決定木の実験場です。 左のキャンバスをクリック(タップ)して2クラスの点を配置し、 最大深さスライダーを動かすと、 その場で決定木が学習され、 軸平行な矩形で塗り分けられた決定境界がリアルタイムに描画されます。 深さを浅くすると粗い境界(未学習・欠学習)、 深くすると点1つ1つを暗記する複雑な境界(過学習)になる様子を体感してください。 計算は本ページの数式どおりの簡易CART(貪欲な二分割)で、 ジニ不純度/情報利得(エントロピー)を切り替えられます。
横軸 x₁ / 縦軸 x₂(クリック=現在のクラスの点を追加)
🌳 学習された木構造(各分割の不純度低下)
💡 観察のヒント:① 2つの塊を離して置き、 深さ1でもきれいに分かれることを確認 → ② 塊を混ぜ合わせ、 深さを上げないと分けられないことを確認 → ③
XOR配置(対角に同クラス)で、 決定木が斜め境界を「階段状の矩形」で近似する様子を観察 → ④ 点を1つだけポツンと敵陣に置き、 深さを最大にすると
その1点のためだけに小さな矩形ができる=
過学習を目撃してください。
🧩 決定木の仕組みを深掘り — 再帰分割から集団学習まで
① 再帰分割:木がデータを切り分ける手続き
決定木の学習は再帰的な二分割の繰り返しです。 (1) 現在のノードに含まれるデータの不純度を測る → (2) すべての特徴量・すべての閾値の組を試し、 分割後の子ノードの重み付き不純度が最小になる分割を貪欲に選ぶ → (3) 左右の子ノードそれぞれで同じ操作を再帰的に繰り返す → (4) 停止条件(max_depth 到達・ノードが純粋・サンプル数が下限未満)で葉ノードにする。 上のプレイグラウンドが行っているのが、 まさにこの手続きです。 各内部ノードは「x₁ < t」型の軸平行な質問1つに対応し、 特徴空間は矩形へと再帰的に分割されます。
② 不純度指標:分割の「良さ」をどう測るか
分割の良し悪しは不純度(impurity)の低下量で測ります。 分類では2つの指標が代表的です。
- ジニ不純度 $\mathrm{Gini}=1-\sum_k p_k^2$:ランダムに1点を取り、 ノードのクラス分布に従ってランダムにラベル付けしたとき誤分類する確率。 CART(sklearn 既定)が採用。 計算が軽い。
- 情報利得:親のエントロピー $H=-\sum_k p_k \log_2 p_k$ から、 分割後の子の重み付きエントロピーを引いた減少量。 ID3/C4.5 が採用。 情報理論的な「不確実性の削減」に対応。
どちらを使っても「子の重み付き不純度を最小化する=親からの低下量を最大化する」という貪欲基準は同じで、 実データ上では選ばれる分割がほぼ一致することが多いです。 プレイグラウンドで両者を切り替えても、 決定境界が大きくは変わらないことを確認できます。
③ 過学習と枝刈り:深さは諸刃の剣
深さを増やすほど訓練データへの当てはまりは良くなりますが、 ある点を超えるとノイズや個別事例まで暗記して汎化性能が落ちます(過学習)。 対策は2系統あります。 事前剪定(pre-pruning)= max_depth・min_samples_leaf・min_samples_split で成長を止める。 事後剪定(post-pruning)= 一度深く育ててから ccp_alpha(コスト複雑度剪定 $R_\alpha(T)=R(T)+\alpha|T|$)で葉を刈り込む。 いずれも正則化の一種で、 最適な強さは交差検証で選ぶのが定石です。
④ アンサンブルへの発展:1本の弱さを束で補う
単体の決定木は高分散(訓練データがわずかに変わると木構造が激変する)という弱点があります。 これを克服するのがアンサンブルです。
解釈性が欲しければ浅い単体木(深さ3〜5)、 予測精度が欲しければアンサンブル、 という使い分けが実務の基本です。
⑤ 長所と短所のまとめ
| ✅ 長所 | ⚠️ 短所 |
| if-then ルールで解釈性が高い(可視化できる) | 高分散で不安定(1本では過学習しやすい) |
| 標準化・正規化などの前処理が不要 | 斜めの境界が苦手(軸平行の階段近似) |
| 非線形・交互作用を自動で捉える | 外挿不可(訓練範囲外は最寄り葉の定数) |
| 数値・カテゴリ・欠損に柔軟(実装による) | 滑らかな線形関係の表現が非効率 |
🔬 数式を言葉で読み解く — 決定木 の記号辞書
上の数式に出てくる各記号が何を表すかを、 言葉で翻訳します。 1 つずつ自分の言葉で言い換えられるようになると、 論文や教科書のスピードが一気に上がります。
| 記号 | 意味(言葉での説明) |
| $p_k$ | ノードでクラス $k$ が占める割合 |
| Gini | $0$ が完全純粋、 大きいほど混在 |
| Entropy | 情報理論的な「乱雑さ」 |
| MSE | 回帰木で使う指標(葉ノードの分散) |
| 深さ | 木の階層数(過学習との戦い) |
📌 読み下しのコツ:左から右に「主語 → 述語 → 目的語」と見立てて、 「これは何を、 どうしている式か?」と一文で要約してみてください。 慣れれば 5 秒で読めます。
🧮 SSDSE-B-2026 実値計算 — 「総人口 200 万人以上」分類木
2023 年度・47 都道府県を「総人口 (A1101) が 200 万人以上 or 未満」の 2 クラスに分け、 年平均気温・消費支出・合計特殊出生率を特徴量に最大深さ 3 の決定木で分岐ルールを抽出します。
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A4103(合計特殊出生率) B4101(年平均気温) L3221(消費支出(二人以上の世帯))
北海道 5,092,000 1.06 11.0 296,888
東京都 14,086,000 0.99 17.6 341,320
沖縄県 1,468,000 1.6 23.8 251,222
…(全 47 行)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | import pandas as pd
from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree
import matplotlib.pyplot as plt
# 1行目=コード, 2行目=日本語名 → コード行を使うため skiprows=[1]
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d23 = df[df['SSDSE-B-2026'] == 2023] # 2023年度・47都道府県
# 目的変数: 総人口(A1101)が200万人以上か
y = (d23['A1101'] >= 2_000_000).astype(int)
# 説明変数: 年平均気温(B4101)・消費支出(L3221)・合計特殊出生率(A4103)
X = d23[['B4101', 'L3221', 'A4103']]
tree = DecisionTreeClassifier(max_depth=3, random_state=42).fit(X, y)
print(export_text(tree, feature_names=list(X.columns)))
print('accuracy =', round(tree.score(X, y), 3))
plot_tree(tree, feature_names=X.columns,
class_names=['200万未満', '200万以上'], filled=True)
plt.show()
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 実行例(実測)
|--- A4103 <= 1.29
| |--- B4101 <= 17.05
| | |--- A4103 <= 1.09
| | | |--- class: 1
| | |--- A4103 > 1.09
| | | |--- class: 0
| |--- B4101 > 17.05
| | |--- A4103 <= 1.27
| | | |--- class: 1
| | |--- A4103 > 1.27
| | | |--- class: 1
|--- A4103 > 1.29
| |--- B4101 <= 14.85
| | |--- class: 1
| |--- B4101 > 14.85
| | |--- L3221 <= 305002.00
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
得られる分岐ルール(例)
|--- A4103 <= 1.29
| |--- B4101 <= 17.05
| | |--- A4103 <= 1.09
| | | |--- class: 1
| | |--- A4103 > 1.09
| | | |--- class: 0
| |--- B4101 > 17.05
| | |--- A4103 <= 1.27
| | | |--- class: 1
| | |--- A4103 > 1.27
| | | |--- class: 1
|--- A4103 > 1.29
| |--- B4101 <= 14.85
| | |--- class: 1
| |--- B4101 > 14.85
| | |--- L3221 <= 305002.00
| | | |--- class: 0
| | |--- L3221 > 305002.00
| | | |--- class: 0
「合計特殊出生率 (A4103) ≦ 1.29」が第 1 分岐 — 出生率の低さが人口 200 万人以上の大都市圏を最もよく切り分ける、と木が自動発見しています。 訓練精度 0.915、 5-fold CV 0.691、 葉ノード 7 個。
🧮 数式に値を入れて手で計算する
SSDSE-B-2026 (2023 年度) の全 47 都道府県を「総人口 (A1101) が 200 万人以上 (クラス高) と未満 (クラス低)」に分け、 その件数 (高 16 件, 低 31 件、 合計 47 県) を Gini 不純度・Entropy の数式に代入し、 Step 1〜3 で手計算する。 これは前節の分類木のルートノードの不純度に一致する。 同じ計算を Python (numpy / sklearn) で再現し、 結果が一致することを確認する。
📐 Gini 不純度・Entropy (再掲)
$$ \mathrm{Gini}(D) = 1 - \sum_{k=1}^{K} p_k^2 \qquad \mathrm{Entropy}(D) = -\sum_{k=1}^{K} p_k \log_2 p_k $$
Step 1: データ準備 (SSDSE-B-2026・2023 年度 47 都道府県の総人口 二値クラス分布)
| クラス k | 件数 $n_k$ | 比率 $p_k = n_k / n$ | $p_k^2$ | $-p_k \log_2 p_k$ |
| 高 (総人口≥200万) | 16 | 16/47 = 0.3404 | 0.1159 | -0.3404 × log₂(0.3404) = 0.5292 |
| 低 (総人口<200万) | 31 | 31/47 = 0.6596 | 0.4350 | -0.6596 × log₂(0.6596) = 0.3960 |
| 合計 | 47 | 1.0000 | 0.5509 | 0.9252 |
Step 2: Gini と Entropy の組み立て
| 項目 | 計算 | 結果 |
| $\sum p_k^2$ | $0.1159 + 0.4350$ | 0.5509 |
| Gini = $1 - \sum p_k^2$ | $1 - 0.5509$ | 0.4491 |
| $-\sum p_k \log_2 p_k$ | $0.5292 + 0.3960$ | 0.9252 |
Step 3: 最終結果と参考値
| 項目 | 結果 | 解釈 |
| Gini | 0.4491 | 最大値 0.5 (50:50) に近く、 やや混在 |
| Entropy | 0.9252 bits | 最大値 1.0 bits に近い (二値時) |
| (参考) 50:50 のとき | Gini=0.5, Entropy=1.0 | 最も不純な状態 |
| (参考) 純粋 (1 クラスのみ) | Gini=0, Entropy=0 | 完全に分かれた葉 |
🐍 同じ計算を Python で再現
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口)
北海道 5,092,000
東京都 14,086,000
沖縄県 1,468,000
…(全 47 行)
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 numpy as np
from sklearn.tree import DecisionTreeClassifier
# 2023年度・全47都道府県を「総人口(A1101) 200万人以上 or 未満」で2クラス化
# df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
# d23 = df[df['SSDSE-B-2026'] == 2023]
# counts = np.array([(d23['A1101'] >= 2_000_000).sum(),
# (d23['A1101'] < 2_000_000).sum()]) # => [16, 31]
counts = np.array([16, 31]) # 高(>=200万) 16, 低(<200万) 31
n = counts.sum()
p = counts / n # 比率 p_k
gini = 1.0 - np.sum(p ** 2)
entropy = -np.sum(p * np.log2(p))
print(f"件数 = {counts.tolist()} (合計 {n})")
print(f"比率 p_k = {np.round(p, 4).tolist()}")
print(f"Gini = {gini:.4f}")
print(f"Entropy = {entropy:.4f} bits")
# 参考: sklearn の DecisionTreeClassifier も同じ不純度を内部で使う
y = np.array([0] * 16 + [1] * 31)
X = np.arange(n).reshape(-1, 1)
clf_g = DecisionTreeClassifier(criterion='gini', max_depth=1).fit(X, y)
clf_e = DecisionTreeClassifier(criterion='entropy', max_depth=1).fit(X, y)
print(f"sklearn root impurity (gini) = {clf_g.tree_.impurity[0]:.4f}")
print(f"sklearn root impurity (entropy) = {clf_e.tree_.impurity[0]:.4f}")
|
📤 実行結果
件数 = [16, 31] (合計 47)
比率 p_k = [0.3404, 0.6596]
Gini = 0.4491
Entropy = 0.9252 bits
sklearn root impurity (gini) = 0.4491
sklearn root impurity (entropy) = 0.9252
💬 手計算 (Step 3 の Gini=0.4491, Entropy=0.9252) と Python (numpy 手計算式・sklearn の tree_.impurity) がすべて完全一致。 二値クラスの最大不純度 (Gini=0.5, Entropy=1.0 bits) に近いことから、 このノードはまだ「分岐すべき余地」が大きい状態だと読める。
🧮 実値で計算してみる — SSDSE-B-2026 で 決定木 を体感
数式だけでは「分かった気になる」だけで終わりがち。 ここで SSDSE-B-2026(教育用標準データセット — 47 都道府県 × 100+ 指標、 2018-2023 年度)の実値を当てはめて、 決定木 の挙動を電卓的に追体験します。
👉 計算例:SSDSE-B-2026(2023 年度)で目的変数を消費支出(L3221)、 説明変数を「総人口(A1101)・65歳以上人口(A1303)・出生数(A4101)・年平均気温(B4101)」に。 深さ 3 の回帰木を構築すると訓練 R² ≒ 0.693 と高く見えるが、 5-fold CV の R² は負値まで落ちる(単木は高分散で過学習しやすい典型)。 精度が要るなら次章の Random Forest 化が定石。
SSDSE-B-2026 は 統計センターの SSDSE 配布ページ から CSV を直接ダウンロードできます。 本サイトでは data/raw/SSDSE-B-2026.csv に配置している前提でコードを書いています。
🐍 Python 実装バリエーション
A. sklearn.tree.DecisionTreeClassifier(基本)
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
| from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='gini', max_depth=5, min_samples_leaf=3,
ccp_alpha=0.0, random_state=42).fit(X, y)
print(tree.feature_importances_)
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 実行例(実測)
[1.]
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
B. sklearn.tree.DecisionTreeRegressor(回帰)
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
| from sklearn.tree import DecisionTreeRegressor
# 消費支出(L3221)を 総人口・65歳以上人口・出生数・年平均気温 から回帰
Xr = d23[['A1101', 'A1303', 'A4101', 'B4101']]
reg = DecisionTreeRegressor(max_depth=3, random_state=42).fit(Xr, d23['L3221'])
print('train R^2 =', round(reg.score(Xr, d23['L3221']), 3)) # => 0.693
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 実行例(実測)
train R^2 = 0.693
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
C. コスト複雑度剪定(ccp_alpha 探索)
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
| path = tree.cost_complexity_pruning_path(X, y)
for a in path.ccp_alphas[::3]:
t = DecisionTreeClassifier(ccp_alpha=a, random_state=42).fit(X, y)
print(f'alpha={a:.4f}, depth={t.get_depth()}, leaves={t.get_n_leaves()}')
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 実行例(実測)
alpha=0.0000, depth=1, leaves=2
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
D. dtreeviz(リッチな可視化)
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
| from dtreeviz import model
viz = model(tree, X_train=X, y_train=y,
target_name='総人口200万以上', feature_names=list(X.columns),
class_names=['200万未満', '200万以上'])
viz.view()
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 このブロックは標準出力には何も出さない
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
E. XGBoost で 1 本だけ作る(GBM の単木)
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 SSDSE-B-2026 の都道府県データで「どの変数が重要か」を可視化できる。
| import xgboost as xgb
model = xgb.XGBClassifier(n_estimators=1, max_depth=3, learning_rate=1.0, random_state=0)
model.fit(X, y)
xgb.plot_tree(model, num_trees=0)
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 このブロックは標準出力には何も出さない
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
🐍 Python 実装 — 決定木 を SSDSE-B-2026 で動かす
まず環境をそろえます。 必要なライブラリを入れ、 matplotlib の日本語フォント(Hiragino Sans)を設定し、 SSDSE を encoding='cp932' で読み込んで shape・head()・describe() を確認します。 この後、 決定木で 47 都道府県を分類・回帰していきます。 決定木は標準化が要らない数少ない手法なので StandardScaler は実際には使いませんが、 他手法と比較する節で必要になります。 エンコーディングを指定し忘れると UnicodeDecodeError で止まるのが最初の関門なので、 ここだけは丸暗記して構いません。
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 | # 決定木 を SSDSE-B-2026 で確かめる最小コード
import pandas as pd
import numpy as np
# 1) SSDSE-B-2026 を読み込み(1行目=コード, 2行目=日本語名 → skiprows=[1])
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
print('shape:', df.shape) # (564, 112) — 47 都道府県 × 12 年度
print('cols head:', list(df.columns[:8]))
# 2) 直近年度(2023 年度)に絞る(年度列の名前は 'SSDSE-B-2026')
df23 = df[df['SSDSE-B-2026'] == 2023].copy()
print('rows in 2023:', len(df23))
# 3) 決定木 を動かすために必要な列だけ取り出す(A4103:合計特殊出生率, A1101:総人口)
y = df23['A4103'].astype(float)
x = df23['A1101'].astype(float)
print('y stats:', y.describe().round(3).to_dict())
print('x stats:', x.describe().round(0).to_dict())
# 4) 決定木 の本処理(このページの主題)
# — 具体実装は同カテゴリの個別ページにも掲載
print('---- 決定木 結果 ----')
print('mean y:', y.mean().round(3), '/ std y:', round(y.std(), 3))
print('mean x:', x.mean().round(0), '/ std x:', round(x.std(), 0))
print('corr(x, y):', y.corr(x).round(3))
|
📤 実行例(実測)
shape: (564, 112)
cols head: ['SSDSE-B-2026', 'Code', 'Prefecture', 'A1101', 'A110101', 'A110102', 'A1102', 'A110201']
rows in 2023: 47
y stats: {'count': 47.0, 'mean': 1.293, 'std': 0.133, 'min': 0.99, '25%': 1.21, '50%': 1.3, '75%': 1.385, 'max': 1.6}
x stats: {'count': 47.0, 'mean': 2645809.0, 'std': 2797551.0, 'min': 537000.0, '25%': 1034000.0, '50%': 1549000.0, '75%': 2636500.0, 'max': 14086000.0}
---- 決定木 結果 ----
mean y: 1.293 / std y: 0.133
mean x: 2645809.0 / std x: 2797551.0
corr(x, y): -0.564
うまく動かないときは ①data/raw/SSDSE-B-2026.csv のパス、 ②encoding='cp932'(SSDSE-B は Shift_JIS 系)、 ③1 行目に英字コード・2 行目に日本語列名が並ぶ構造で、 skiprows=[1](2 行目だけ捨てて 1 行目のコードを列名にする)が必要、 の 3 点を確認してください。
⚠️ 決定木の落とし穴 7 連発
1. 制限なし(max_depth=None)で訓練して過学習。デフォルトの sklearn は葉が純粋になるまで分割し続けるため、 訓練精度 100%・テスト精度 60% のような典型的過学習が起きます。 max_depth・min_samples_leaf・ccp_alpha のいずれかで剪定しないと使い物になりません。
2. 1 本の木の解釈を「真のルール」だと信じる。訓練データが少し変わると、 ルート分岐の特徴量自体が入れ替わるほど不安定です。 Random Forest 化したうえで Permutation Importance を取り、 安定して上位にくる変数を解釈の主役に据えましょう。
3. カテゴリ変数を「ラベルエンコード」して入れる。名義尺度(県名・血液型)に整数 0..n を割り当てると、 決定木は「県コード < 24」のような疑似的順序関係を学習してしまいます。 One-hot か、 ターゲットエンコーディングのほうが適切です(CatBoost なら自動)。
4. 外挿(範囲外予測)を行う。決定木は区分定数関数なので、 訓練データの最大値より大きい x を入れても、 「一番右の葉」と同じ値を返すだけ。 時系列の右端予測や、 集計値の極端領域では線形モデルとアンサンブルする方が安全です。
5. ノイズ変数を全部投入する。決定木は最も分散減少を稼げる特徴を貪欲に選ぶため、 ID 列・ハッシュ・ノイズ的ダミーがあると、 偶然それで分割して訓練データに過適合します。 事前に相関スクリーニングや MI(mutual_info)で篩にかけましょう。
6. クラス不均衡で多数派ばかり予測する。陽性 5% のデータでは、 デフォルト設定だと木が「全件陰性」の浅い構造に潰れます。 class_weight='balanced'、 SMOTE、 閾値調整のいずれかで対応してください。
7. random_state 未固定で結果がぶれる。同点ブレークの分岐で乱数が使われるため、 再実行で異なる木になります。 比較・論文用の数値は必ず random_state=42 等を固定し、 加えて乱数を変えた 5 回の平均を併記しましょう。
⚠️ よくある落とし穴 — 決定木 で初学者がやりがちなミス
この用語を実務で使うときにつまずきやすい点を、 失敗パターン別に整理しました。 1 度経験すれば回避できるものばかりですが、 先に知っておくと事故が大幅に減ります。
❌ 深さ制限なし
無制限だと訓練データに完全フィットして過学習。 max_depth=3〜5 が安全。
❌ 不安定性
入力をわずかに変えるだけで木の構造が大きく変わる。 Random Forest で安定化。
❌ 軸並行分割
斜めの境界は表現できない。 SVM や NN で補完。
❌ 線形関係が苦手
滑らかな単調関係は階段近似になる。 線形モデルと併用。
🛡 防御策まとめ:「適用条件の確認 → 適切な前処理 → 結果と前提のペア記述」の 3 ステップを習慣にすれば、 ここに挙げた失敗の大半は回避できます。
📚 統計学習の総合ガイド
📚 統計学習の総合ガイド
🎯 学習目標
このページの概念をマスターすることで、 以下のスキルが身につきます:
- 定義と公式を正確に理解
- 適切な使用場面を判断
- Python で実装し、 結果を可視化
- 仮定の確認と診断
- 結果の解釈と報告
- 限界と注意点の理解
- 関連手法との使い分け
📊 SSDSE-B-2026 データの構造
このコンペの主要データセット(SSDSE-B-2026)の構造:
- 47都道府県 × 過去複数年(パネル形式)
- 112列の社会経済指標
- 人口、 出生、 死亡、 婚姻、 経済、 教育、 環境、 家計など多次元
- 政府統計を統合した信頼性の高いデータ
🔍 主要な変数群
| カテゴリ |
変数例 |
| 人口 | 総人口、 年齢別人口、 性別人口 |
| 人口動態 | 出生数、 死亡数、 合計特殊出生率、 婚姻数 |
| 気候 | 気温、 降水量、 降水日数 |
| 教育 | 幼小中高校数、 教員数、 生徒数、 大学進学率 |
| 経済 | 求職件数、 求人件数、 旅館数 |
| 医療 | 病院数、 診療所数、 歯科診療所 |
| 家計 | 消費支出、 食料費、 住居費、 教育費等の項目別 |
💡 ジャストインタイム型学習
このガイドは「必要なときに必要な知識」を提供する設計:
- 論文中の用語をクリック → 該当の用語解説へジャンプ(ポップアップ)
- 概念マップで関連用語を辿る
- 包含マップで体系を把握
- ツリーマップで全体を俯瞰
- Python コードをコピーして実行
- SSDSE データで実際に試す
🛠️ Python データサイエンス環境
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 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())
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 このブロックは標準出力には何も出さない
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
🌟 効果的なEDAテンプレート
🎯 解説: 決定木(Decision Tree)は if-then ルールで分岐する解釈性の高い分類・回帰モデル。 ジニ不純度/エントロピーで最適分岐を選ぶ。 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
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()
|
📥 入力例: data/raw/SSDSE-B-2026.csv
X: 複数変数
y: ターゲット
📤 このブロックは標準出力には何も出さない
💬 読み方: 読み方: max_depth が小さいと欠学習、 大きいと過学習。 min_samples_split で分岐の最小サンプル数を制御。 木を可視化(plot_tree)すると「どの変数で分岐したか」が一目瞭然。
📈 報告書テンプレート
分析結果を報告する際の標準的な構成:
- 背景・目的:なぜこの分析が必要か
- データ:出所、 サンプルサイズ、 期間
- 方法:使用した統計手法、 仮定
- 結果:図表、 統計量、 検定結果
- 解釈:結果が何を意味するか
- 限界:分析の制約
- 結論:要点まとめ、 今後の課題
決定木 は単独で完結する用語ではなく、 大きな概念群の中のひとつ。 同じカテゴリの他用語と合わせて学ぶと、 「何を選ぶべきか」の判断が一気に楽になります。
📘 決定木の剪定(pre-pruning vs post-pruning)と CART/ID3/C4.5
決定木は深く育てると訓練データに完全フィットしやすい。 過学習を抑える 2 つの戦略が 事前剪定(pre-pruning)と 事後剪定(post-pruning)。 sklearn の DecisionTreeClassifier は両方をサポートしており、 特に CART の cost-complexity pruning(ccp_alpha)が事後剪定の代表的実装。 ここでは SSDSE-B-2026 を用い、 都道府県を「人口規模」で 2 クラスに分けて木の深さと精度のトレードオフを観察する。
A. アルゴリズム比較(CART / ID3 / C4.5)
| アルゴリズム | 分割基準 | 分岐数 | 連続値 | 欠損値 | 剪定 |
| ID3 | 情報利得(IG) | 多分岐 | × | × | なし |
| C4.5 | 情報利得比(Gain ratio) | 多分岐 | ○ | ○ | 事後(誤差ベース) |
| CART (sklearn) | Gini / MSE | 二分岐 | ○ | △(sklearn 1.4+) | 事後(cost-complexity) |
sklearn の DecisionTreeClassifier は CART 系の二分岐木のみ実装。 多分岐木が必要な場合は orange3 や C50(R)を検討する。
B. 事前剪定: max_depth / min_samples_leaf
このコードでやること: SSDSE-B-2026 の都道府県を「総人口 (A1101) 200 万人以上=1, 未満=0」に分類する木を、 年平均気温 (B4101)・消費支出 (L3221)・合計特殊出生率 (A4103) から max_depth を 1〜10 に変えて訓練・テスト精度を比較。
📥 入力データ(SSDSE-B-2026, 2023 年度 47 都道府県 × 3 特徴量):
Prefecture B4101 L3221 A4103 high_pop
0 北海道 11.0 296888 1.06 1
1 青森県 12.6 263371 1.23 0
2 岩手県 12.5 298536 1.16 0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d23 = df[df['SSDSE-B-2026'] == 2023]
y = (d23['A1101'] >= 2_000_000).astype(int) # 総人口200万以上=1
X = d23[['B4101', 'L3221', 'A4103']] # 気温・消費支出・出生率
X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y)
for d in [1, 2, 3, 5, 10]:
clf = DecisionTreeClassifier(max_depth=d, random_state=42).fit(X_tr, y_tr)
print(f'depth={d:2d} train={clf.score(X_tr, y_tr):.3f} test={clf.score(X_te, y_te):.3f}')
|
📤 実行結果:
depth= 1 train=0.812 test=0.733
depth= 2 train=0.844 test=0.733
depth= 3 train=0.969 test=0.867
depth= 5 train=1.000 test=0.867
depth=10 train=1.000 test=0.867
💬 深さ 3 でテスト精度が最大(86.7%)。 深さ 5 以上は訓練 100% でもテストは 86.7% で頭打ち=訓練データだけを丸暗記する過学習。 SSDSE のように N=47 と小さいデータでは max_depth=3 前後が妥当。
C. 事後剪定: ccp_alpha (Minimal Cost-Complexity Pruning)
このコードでやること: 一度フル成長させた木を、 cost_complexity_pruning_path で得た α 候補に沿って剪定し、 最適な α をテスト精度で選ぶ。 数式は
$$R_\alpha(T) = R(T) + \alpha \cdot |T|$$
数式を言葉で読み解く
R(T) は木 T の誤差(不純度の合計)、 |T| は葉ノード数、 α は複雑さペナルティ係数。 α が大きいほど葉が多い木に重い罰を与え、 単純な木が選ばれる。 α=0 なら剪定なし、 α→∞ なら根ノードのみ。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行)
都道府県 A1101(総人口) A4103(合計特殊出生率) B4101(年平均気温) L3221(消費支出(二人以上の世帯))
北海道 5,092,000 1.06 11.0 296,888
東京都 14,086,000 0.99 17.6 341,320
沖縄県 1,468,000 1.6 23.8 251,222
…(全 47 行)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d23 = df[df['SSDSE-B-2026'] == 2023]
y = (d23['A1101'] >= 2_000_000).astype(int)
X = d23[['B4101', 'L3221', 'A4103']]
X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y)
clf = DecisionTreeClassifier(random_state=42)
path = clf.cost_complexity_pruning_path(X_tr, y_tr)
for a in path.ccp_alphas[:-1]:
m = DecisionTreeClassifier(ccp_alpha=a, random_state=42).fit(X_tr, y_tr)
print(f'alpha={a:.4f} leaves={m.get_n_leaves():2d} test={m.score(X_te, y_te):.3f}')
|
📤 実行結果:
alpha=0.0000 leaves= 8 test=0.867
alpha=0.0268 leaves= 6 test=0.867
alpha=0.0536 leaves= 5 test=0.867
alpha=0.0583 leaves= 4 test=0.867
alpha=0.0690 leaves= 2 test=0.733
💬 α=0〜0.058 では葉数を 8→4 に減らしてもテスト精度 86.7% を維持、 α=0.069 で 2 葉まで潰すと 0.733 に低下。 事前剪定 (depth=3) と同程度の性能を、 「全成長 → 事後剪定」というルートでより単純な木として再現できた。 cross-validation で α を選ぶのが本式。
D. 落とし穴
- 深さで止めるか α で止めるか:
max_depth は速いが粗い、 ccp_alpha は CV で選ぶと精緻。 小規模データなら ccp_alpha 推奨。
- クラス不均衡で Gini が騙される: 90:10 のクラス比だと「全部多数派」予測でも Gini が低くなる。
class_weight='balanced' を併用する。
- カテゴリ変数の扱い: sklearn は数値のみ → カテゴリは one-hot か
OrdinalEncoder 前処理が必要。 R の rpart は文字列を直接扱える点が違う。
🗺️ 統計手法選択フローチャート
Q1: 何を知りたい?
- 記述したい → 平均、 分散、 ヒストグラム
- 比較したい → t検定、 ANOVA、 χ²検定
- 関係を見たい → 相関、 回帰
- 予測したい → 回帰、 機械学習
- 分類したい → ロジスティック回帰、 SVM、 RF
- グループ分けしたい → クラスタリング
- 次元を減らしたい → PCA、 因子分析
- 因果関係を知りたい → RCT、 IV、 DiD、 PSM
Q2: データの種類は?
- 連続値 → t検定、 ANOVA、 線形回帰
- カテゴリ → χ²検定、 ロジスティック回帰
- 順序 → ノンパラ検定、 順位回帰
- カウント → ポアソン回帰、 負の二項回帰
- 時系列 → ARIMA、 VAR、 状態空間
- パネル → 固定効果、 ランダム効果
Q3: サンプルサイズは?
- n < 30:ノンパラ、 ベイズ、 ブートストラップ
- 30 ≤ n < 200:古典的検定、 単純な回帰
- n ≥ 200:複雑なモデル、 機械学習
- n ≥ 10000:深層学習も可能
Q4: 仮定は?
- 正規性:満たす → パラメトリック / 満たさない → ノンパラ
- 独立性:必須 / 違反 → クラスター調整、 時系列モデル
- 等分散性:満たす → OLS / 違反 → WLS、 ロバスト
📏 効果量の参照表
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 |
🚀 実務応用の深掘り
典型的なプロジェクトの流れ
- 問題理解:ステークホルダーとの対話、 KGI/KPI 設定
- データ収集:内部DB、 公的データ(SSDSE等)、 API
- EDA:データの全体像把握、 異常検出
- 仮説立案:ドメイン知識からの仮説
- モデリング:シンプルから複雑へ段階的に
- 検証:CV、 ホールドアウト、 A/Bテスト
- 解釈:可視化、 SHAP、 部分依存プロット
- 展開:本番デプロイ、 監視
ベストプラクティス
- シンプルなモデルから始める(線形回帰、 単純ルール)
- 必ずベースラインと比較
- 過学習を防ぐ(CV、 正則化、 早期停止)
- 解釈可能性を重視
- 再現可能なコード・ノートブック
- バージョン管理(Git)と環境管理(venv, conda)
- ドキュメント化を怠らない
論文・コンペでよく使う言い回し
| 日本語 |
英語 |
| 統計的に有意 | statistically significant |
| 効果量 | effect size |
| 95%信頼区間 | 95% confidence interval (CI) |
| 標本サイズ | sample size |
| 検出力 | statistical power |
| 第1種の誤り | Type I error / false positive |
| 第2種の誤り | Type II error / false negative |
| 多重比較問題 | multiple comparisons problem |
| 過学習 | overfitting |
| 汎化性能 | generalization |
| 交差検証 | cross-validation (CV) |
統計データ活用コンペでのコツ
- SSDSE データの構造を理解し、 適切なテーブルを選ぶ
- 地域別・年度別の比較で時空間的視点を入れる
- 1つの分析で多角的に切り口を変える
- 仮説と発見の両方を持つ
- ストーリーラインを明確に
- 図表を1枚1枚作り込む
- 政策提言や実務的意義に繋げる
🗺️ 概念マップ — 3つの視点で体系を理解する
決定木 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
📍 体系階層のパス
🌐 統計・データサイエンス › 機械学習 › 教師あり学習 › 決定木
① 🔗 関係マップ — 「他の手法とどう繋がっているか」
中心に 決定木 を置き、 そこから 分類・ランダムフォレスト・XGBoost 計 3 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語とあとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。
凡例:現在の用語上位カテゴリ兄弟(並列)前提発展形応用先2階層先
② ⭕ 包含マップ — 「どのカテゴリに含まれているか」
大きな円が小さな円を包含する Circle Packing 図。 「決定木」は緑色でハイライト。
- カテゴリ円をクリック:その内部にズームイン
- 白背景クリック:1階層戻る
- 用語円をクリック:詳細ページへ遷移
- マウスホバー:階層パス表示
③ 🌳 ツリーマップ — 「面積で見るボリューム比較」
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「決定木」は緑色でハイライト。
- カテゴリ矩形をクリック:その内部にドリルダウン
- パンくず(上のリンク)クリック:その階層に戻る
- 用語矩形をクリック:詳細ページへ遷移
- マウスホバー:階層パスと値を表示
🎯 3つのマップの使い分け
| マップ |
分かること |
こんな時に見る |
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは 決定木 の隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンス → 教師あり学習 → 決定木 という入れ子の位置を示します。 「教師あり学習には他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。
🔗 隣接手法への橋渡し
「決定木」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。 具体的には次の 3 方向と密接につながる:
決定木は単純で解釈性が高く、 アンサンブル手法の基底学習器として発展する。
🌳 手法選択フロー
「decision tree」を含む手法選択は、 データの性質・分析目的・運用制約の 3 軸で決まる。 以下に典型シナリオと対応する手法の組合せを示す。
典型シナリオ別の手法選択
| シナリオ | 重視する観点 | 候補手法 |
| データが大規模 (n が多い、 高次元) | 計算コスト / メモリ / 並列化が必要 | Random Forest 等 |
| 外れ値が多い / ノイズあり | 頑健性 / 外れ値除去 / 中央値ベース | XGBoost 等 |
| 解釈性を重視する | 線形 / 木構造 / ルールベース | CART 等 |
| 予測精度を最優先する | アンサンブル / ハイパラ調整 / 交差検証 | C4.5 等 |
| データが少ない | 正則化 / ベイズ / 転移学習 / 簡素なモデル | ID3 等 |
| リアルタイム/オンライン処理 | ストリーミング / 軽量モデル / 逐次更新 | CHAID 等 |
選んだ後の検証ステップ
- 前処理確認: 標準化 / 欠損処理 / カテゴリ変数のエンコード が適切か
- ハイパラ調整: 交差検証で安定する値を選ぶ
- 性能評価: タスクに応じた指標 (RMSE / Accuracy / F1 / AUC / シルエット等) を複数併用
- 頑健性チェック: 別手法 / 別シードで結果が大きく違わないか確認
- 解釈: 結果を分野知識と照らし合わせ、 意味のある発見になっているか検討
🎨 直感をもう一段深める — 「20の質問」ゲームとしての決定木
決定木は「はい/いいえで答える20の質問ゲーム」そのものです。 相手が思い浮かべた都道府県を当てるとき、 私たちは「人口は多い?」「海に面している?」と最も候補を半分に絞れる質問から順に投げます。 決定木の学習アルゴリズムがやっているのは、 この「一番よく候補を絞れる質問(=不純度を最も下げる分割)」を各段で貪欲に選ぶことに他なりません。 良い質問ほど答えを聞いた後の迷い(不純度)が小さくなる、 という感覚がそのまま ジニ不純度 や 情報利得 の最小化に対応します。
🔑 3つのキーイメージ
- 切り分け:特徴空間を軸に平行なハサミで何度も切り、 最後に残った各区画(葉)に「多数決クラス」または「平均値」の札を貼る。
- 再帰:切った左右の区画それぞれで、 また同じ「一番良い切り方探し」を繰り返す。 木が枝分かれするのはこのため。
- 停止:区画が純粋になる/深さ上限/サンプルが少なすぎる、 で切るのをやめる。 いつやめるかが過学習を左右する最重要のツマミ。
上の 🎮 プレイグラウンド で点を配置し深さを動かすと、 この「切る→区画に札を貼る」過程が目に見えます。 if-then ルールの列として書き下せる(=人間が読める)のが、 他の非線形モデル(SVM・ニューラルネット)に対する決定木の決定的な強みです。 一方で「切り口が必ず軸平行」という制約が、 次に述べる落とし穴の根っこにもなっています。
⚠️ 落とし穴を実データで見る — SSDSE-B-2026 で過学習を再現
「深い木は過学習する」は言葉で言うと当たり前ですが、 実データで数値として見ると腑に落ちます。 ここでは SSDSE-B-2026(2023年度・47都道府県)の実測値だけを使い、 深さを変えたときの訓練性能と汎化性能(Leave-One-Out 交差検証, n=47 なので LOO が自然)のギャップを示します。 数値はすべて下のコードで再現できます。
① 分類木:総人口が 200 万人以上か(葉=多数決クラス)
目的変数=総人口 (A1101) ≧ 200 万人(該当 16/47 県)。 特徴量=年平均気温 (B4101)・消費支出 (L3221)・合計特殊出生率 (A4103)。
| max_depth |
訓練 Accuracy |
LOO-CV Accuracy |
葉の数 |
解釈 |
| 1 | 0.766 | 0.745 | 2 | やや欠学習 |
| 2 | 0.872 | 0.766 | 4 | LOO 最良(汎化のピーク) |
| 3 | 0.915 | 0.723 | 7 | 訓練↑・CV↓(乖離開始) |
| 5 | 0.979 | 0.596 | 11 | 過学習 |
| None(無制限) | 1.000 | 0.638 | 12 | 訓練を完全暗記=典型的過学習 |
→ 訓練 Accuracy は深さとともに単調に 1.000 まで上がるのに、 LOO-CV は depth=2 の 0.766 が頂点で以後は下降。 まさに 図 3 の「訓練誤差↓・汎化誤差∪」を実データが再現しています。 深さ 3 の木の根分岐は「合計特殊出生率 A4103 ≦ 1.29」で、 特徴重要度は A4103=0.608・B4101=0.371・L3221=0.021。 このデータでは気温と出生率が効き、 消費支出はほぼ使われません。
② 回帰木:合計特殊出生率 A4103 を予測(葉=平均値)
目的変数=合計特殊出生率 A4103(実測レンジ 0.99〜1.60、 平均 1.293)。 特徴量=気温 (B4101)・消費支出 (L3221)・総人口 (A1101)。 指標は決定係数 $R^2$。
| max_depth |
訓練 $R^2$ |
LOO-CV $R^2$ |
葉の数 |
| 1 | 0.352 | 0.208 | 2 |
| 2 | 0.612 | 0.362 | 4 |
| 3 | 0.757 | 0.341 | 7 |
| 5 | 0.914 | 0.160 | 17 |
| None(無制限) | 1.000 | 0.060 | 43 |
→ 回帰でも同じ形。 訓練 $R^2$ は 1.000(47点を43葉でほぼ 1 点 1 葉に暗記)まで上がる一方、 LOO-CV $R^2$ は depth=2 の 0.362 が頂点で、 無制限では 0.060 まで崩落します。 「葉が増える=暗記が進む」が数字で見えます。 なお回帰木は区分定数関数なので、 訓練データの外側(外挿)では常に端の葉の定数を返す点にも注意(落とし穴 4)。
③ ジニ vs エントロピー:基準を変えると木も少し変わる
同じ分類問題(深さ 3)で分割基準を切り替えた実測比較です。
| 基準 |
根の分岐 |
訓練 Accuracy |
葉の数 |
| ジニ不純度(sklearn 既定) | A4103 ≦ 1.29 | 0.915 | 7 |
| エントロピー(情報利得) | A4103 ≦ 1.35 | 0.787 | 4 |
→ どちらも根で選ぶ特徴は同じ(A4103)で、 一般に両者の差は小さいと言われます。 ただし小標本(n=47)では閾値・葉数・同点処理の違いから木の形が変わることもあり、 「基準の選択そのものより深さ・剪定の方が影響が大きい」という経験則を裏づけます。
④ 特徴量重要度の落とし穴:高カーディナリティ有利バイアス
不純度ベースの特徴量重要度(sklearn の feature_importances_)は、 取りうる値が多い変数(連続値・高カーディナリティ)ほど分割の機会が多く、 実際には無関係でも重要度を稼いでしまうという既知のバイアスがあります。 実データ 3 変数に、 予測に一切関係しない 架空のほぼ一意な一様乱数列を 1 本足して無制限深さで学習すると:
A4103 = 0.535 / B4101 = 0.301 / L3221 = 0.121 / 架空ノイズ = 0.043(真の寄与はゼロのはず)
→ 無意味なノイズ列が 重要度 0.043 を誤って獲得しました。 対策は、 (1) Permutation Importance や SHAP 値など木構造に依存しない重要度を併用、 (2) 高カーディナリティのカテゴリは安易なラベルエンコードを避ける(落とし穴 3)、 (3) ID 列やハッシュを特徴に混ぜない(落とし穴 5)。
🔁 再現コード(この節の数値はすべてここから)
# SSDSE-B-2026 実測: 深さ vs 過学習(分類・回帰)
import pandas as pd, numpy as np
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
from sklearn.model_selection import cross_val_score, cross_val_predict, LeaveOneOut
from sklearn.metrics import r2_score
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023]
feats = ['B4101', 'L3221', 'A4103'] # 気温・消費支出・出生率
X = d[feats].values
y = (d['A1101'] >= 2_000_000).astype(int).values # 総人口200万以上か
for dep in [1, 2, 3, 5, None]:
clf = DecisionTreeClassifier(max_depth=dep, random_state=42).fit(X, y)
train = clf.score(X, y)
loo = cross_val_score(clf, X, y, cv=LeaveOneOut()).mean()
print(f'depth={dep}: train={train:.3f} LOO={loo:.3f} leaves={clf.get_n_leaves()}')
🚀 発展 — 枝刈り・アンサンブル・重要度の計算
① コスト複雑度剪定(Cost-Complexity Pruning, CCP)
事後剪定の代表格。 木 $T$ の「当てはまりの悪さ $R(T)$」に「木の大きさ $|T|$(葉の数)」への罰則を足した目的関数
$$ R_\alpha(T) = R(T) + \alpha\,|T| $$
を最小化します。 $\alpha=0$ なら剪定なし(フルサイズ)、 $\alpha$ を上げるほど葉が減り単純化します。 sklearn では cost_complexity_pruning_path() で有効な $\alpha$ 候補列を取得し、 交差検証で最良の ccp_alpha を選ぶのが定石です(上の 🐍 Python 実装節にコード例あり)。 事前剪定(max_depth / min_samples_leaf)が「育てる前に止める」のに対し、 CCP は「深く育ててから刈る」ため、 重要な深い分割を保ちつつ無駄枝だけ落とせる利点があります。 いずれも本質は 正則化 です。
② アンサンブル化:高分散を「束ねて」抑える
上の実測で見たとおり単体木は不安定(高分散)です。 これを克服する 2 系統が実務の主役です。
両者の位置づけは バイアス・バリアンス分解 の観点で整理でき、 木のアンサンブル全体像は 木のアンサンブル ページにまとまっています。 「単体木は解釈用、 アンサンブルは精度用」が使い分けの一言まとめです。
③ アルゴリズム系譜:CART / ID3 / C4.5
- CART:二分木限定。 分類はジニ、 回帰は分散減少(MSE)。 sklearn が採用する系統。 回帰木もこの枠組み。
- ID3:情報利得基準。 多分岐・カテゴリ変数向け。 連続値・剪定は非対応で過学習しやすい。
- C4.5:ID3 の後継。 情報利得比(gain ratio)で高カーディナリティ有利バイアスを緩和し、 連続値・欠損・悲観的剪定に対応。
④ 回帰木と特徴量重要度の計算
回帰木は葉に「そのノードの目的変数の平均値」を割り当て、 分割基準を不純度から分散(MSE)減少に置き換えたものです(上の実測②がまさに回帰木)。 不純度ベースの特徴量重要度は、 その特徴で分割したノードでの不純度減少量をサンプル数で重み付けして合計・正規化したもの。 計算は軽い反面、 落とし穴④の高カーディナリティ・バイアスを持ちます。 より頑健な代替が Permutation Importance(特徴をシャッフルして性能低下を測る)と SHAP 値(各予測への寄与を公平に分配)です。
🔗 あわせて読みたい(関連ページ)
本ページの理解を前提・並列・発展の 3 方向へ広げるための、 サイト内実在ページへのリンクです。