この用語と一緒に検索・参照されやすいタグ。 関連ページに飛ぶときの手がかりにも使えます。
「feature」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「feature」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「feature の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
データを数値の表に直したものです。
AIに学習させるために使います。
スマホの利用時間を数値にする例です。
ここでは特徴量の種類や作り方を学びます。
特徴量は、 機械学習モデルへの入力ベクトルの各成分。 「データを数値テーブルに翻訳した結果」。
ここまでが要点です。 ただし実際に使う前に、 このページの「⚠️ よくある落とし穴」で挙げた リーケージ/高カーディナリティのカテゴリ/スケーリングの忘れ には必ず目を通してください。 つまずくのは知識が無いときより、 知ってはいたが確認を飛ばしたときです。
🍰 まずはやさしく
予測の精度を決める重要な道具です。
より正確な答えを出すために使います。
部活の得点から勝ち負けを予測する例です。
どんな場面でどう使うかを学びます。
回帰・分類いずれでも「予測精度を決めるのは結局のところ特徴量」と言われます。 SSDSE データでも、 「人口」より「高齢化率=人口比」の方が死亡率の予測に有効、 といった事例が頻出します。
特徴量 (feature) は単独では意味を持たず、 「測定対象 (個体・時点)」「尺度 (連続・カテゴリ・順序)」「ターゲット変数との関係 (予測対象 y)」「学習アルゴリズムが要求する形 (スケーリング・エンコーディング)」と組み合わせて初めてモデルに効きます。 「定義を覚える」より「どんな予測問題で、 どう変形して使う道具か」を意識するのが効率的です。
🍰 まずはやさしく
料理に使う素材のようなものです。
質の良いデータを用意するために使います。
誕生日を年齢に書き換える例です。
イメージで特徴量の正体を掴みましょう。
「特徴量」を最初に学ぶときは、 厳密な定義よりイメージを優先しましょう。 以下は具体例・比喩を用いた直感的理解の入口です。
| 方式 | 考え方 | 代表手法 | 計算量 | 特徴量同士の関係 |
|---|---|---|---|---|
| Filter | モデル無しで統計指標だけで選ぶ | 相関係数、 F-統計量、 Mutual Information、 χ² | O(Np) | 個別評価 (相関は考慮しない) |
| Wrapper | モデルを実際に学習して試行錯誤で選ぶ | RFE (Recursive Feature Elimination)、 Forward/Backward Selection | O(p²) 以上 | 同時に考慮 |
| Embedded | モデル学習と同時に選ぶ | Lasso (L1)、 Tree-based feature_importances_、 ElasticNet | O(Np) (モデル次第) | 同時に考慮 |
💡 使い分けの定石: ① まず Filter で 1000 → 100 特徴量に削減、 ② Embedded (Lasso/Tree) で 100 → 30 に絞る、 ③ 最後に Wrapper (RFE) で 30 → 10 に詰める。 1 段階目を省くと p が大きすぎて Wrapper/Embedded が回らない。
| 生データ | 特徴量化 | ドメイン根拠 |
|---|---|---|
| 20-34歳女性人口 | そのまま使う | 出産可能年齢の母数 |
| 婚姻件数 | 未婚率に変換 | 出産前提に婚姻が多い社会 |
| 保育所定員(絶対数) | 0-5歳児あたりに変換 | 需要供給比が本質 |
| 平均通勤時間 | そのまま使う | 共働き・育児負担の代理指標 |
| 都道府県名 | OneHot → 47 列 | 地域固定効果 |
右列の「変換」を経た特徴量で線形回帰の R² が 0.55 → 0.78 に上がる経験は普通。 「生 CSV をそのまま fit」では捉えられない構造を、 ドメイン知識が引き出す。
就活の履歴書は「生データ」、 エントリーシート(自己 PR)は「特徴量」と思うとよい。 生年月日(生)から「就活時年齢 22 歳」(特徴量)、 部活動経験から「リーダーシップ経験あり」(特徴量) と意味ある形に圧縮するのが特徴量。 採用担当者(モデル)は履歴書の生データだけ見ても判断しづらいので、 エントリーシートで構造化された情報を読む。 もし君が応募側なら、 同じ経歴をどう「言語化」するかで通過率が変わるはず。 ML も同じだ。
🍰 まずはやさしく
データを数式で表したものです。
正確な意味を共通して伝えるために使います。
テストの点数を並べてベクトルにする例です。
記号の意味と定義を詳しく読み解きます。
直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを「🔬 数式を言葉で読み解く」で 1 つずつ確認してください。
サンプル $i$ ($i=1,\dots,N$) の特徴ベクトルを $\mathbf{x}_i = (x_{i1}, x_{i2}, \dots, x_{ip}) \in \mathcal{X} \subseteq \mathbb{R}^p$ と書く。 目的変数を $y_i$ とすると、 教師あり学習は写像
$$ f: \mathcal{X} \to \mathcal{Y}, \quad \hat{y}_i = f(\mathbf{x}_i; \boldsymbol{\theta}) $$
を、 損失 $\mathcal{L}(\hat{y}, y)$ が最小になるパラメータ $\boldsymbol{\theta}^*$ で推定する問題です。 特徴量エンジニアリングは「変換 $\phi: \mathcal{X}_{\text{raw}} \to \mathcal{X}$ をうまく設計して、 同じ $f$ でもより良い精度が出るようにする」作業と言える。
| 記号 | 読み方 / 役割 |
|---|---|
$x_{ij}$ | $i$ 番目のサンプル (例: 北海道 2023 年) の $j$ 番目の特徴量 (例: 総人口 5,092,000) |
$p$ | 特徴量の本数 (次元数)。 SSDSE-B-2026 では最大 108 (メタ 3 列と target を除く) |
$N$ | サンプル数。 SSDSE-B-2026 では 564 (47 県 × 12 年)、 単年なら 47 |
$\phi$ | 特徴抽出関数。 log 変換、 標準化、 比率作成などすべてここに入る |
$\mathcal{X}$ | 特徴空間。 $\mathbb{R}^p$ の部分集合。 「特徴量同士の幾何学的な広がり」 |
💡 「数式を言葉で読み解く」 ことで分かる重要点: 特徴量エンジニアリングは「$\phi$ という前処理関数を設計する」工程であり、 モデル $f$ の選択と独立に性能を改善できる。 だからこそ Kaggle 上位陣は「モデルより特徴量に時間を割く」と口を揃える。
数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。
機械学習における特徴量 (Feature) とは、 「モデルへの入力として使う列」のこと。 raw データから「意味のある数値」を切り出し、 場合によっては変換 (log / 比率 / 交互作用) してモデルが学習しやすい形に整える作業を 特徴量エンジニアリング (Feature Engineering) と呼びます。 ここでは data/raw/SSDSE-B-2026.csv (564 行 × 112 列) を題材に、 ① 数式での定義、 ② Filter / Wrapper / Embedded の 3 大選択法、 ③ 多重共線性 (VIF) と Mutual Information の比較、 ④ Leakage を防ぐ Pipeline 設計、 ⑤ SHAP/Permutation Importance での重要度可視化、 を実コード付きで解剖します。
⚠️ 落とし穴: feature_importances_ (gain/split) は「モデル全体でどの特徴がよく使われたか」しか教えてくれない。 「この 1 サンプルがなぜ高く予測されたか」は分からない。 そこを補うのが SHAP (SHapley Additive exPlanations)。 ゲーム理論の Shapley 値を機械学習に持ち込み、 各特徴量の 寄与を加法分解する。
このコードでやること: SSDSE-B-2026 の都道府県データで A4101 (出生数) を予測する Random Forest を学習し、 SHAP で「東京がなぜ突出して予測されたか」を可視化する。 特徴量は総人口 (A1101)・15歳未満人口 (A1301)・65歳以上人口 (A1303) の 3 実在列。
📥 入力データ (SSDSE-B-2026 / 2023 年 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd from sklearn.ensemble import RandomForestRegressor import shap df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True) X = d23[['A1101', 'A1301', 'A1303']] # 総人口, 15歳未満人口, 65歳以上人口 y = d23['A4101'] # 出生数 model = RandomForestRegressor(n_estimators=200, random_state=42).fit(X, y) explainer = shap.TreeExplainer(model) shap_values = explainer.shap_values(X) # 東京都の寄与分解 tokyo = d23.index[d23['Prefecture'] == '東京都'][0] for feat, sv in zip(X.columns, shap_values[tokyo]): print(f'{feat}: {sv:+.0f} 人') # expected_value は ndarray で返ることがあるので float() を通す base = float(explainer.expected_value) print(f'予測値 = ベース({base:.0f}) + 寄与合計({shap_values[tokyo].sum():+.0f}) = {base + shap_values[tokyo].sum():.0f} 人') print(f'実測の出生数 = {y[tokyo]:.0f} 人') |
📤 実行結果 (SSDSE-B-2026 実データで算出):
💬 結果の読み方: 東京都の予測出生数はベース値 15341 人に各特徴量の寄与を足し上げた値。 総人口 (+22967 人) と 15歳未満人口 (+22896 人) が二大寄与で、 「東京の出生数が多いのは人口規模と子ども世代の厚みが効いている」とサンプル単位で言える。 これが SHAP の強み。 feature_importances_ だけだと「全体で人口が重要」までしか言えず、 個別案件への説明責任 (XAI / accountability) を果たせない。 金融与信や医療判定など説明責任が問われる場面では SHAP が事実上の標準。
特徴量に関する代表的な誤解を潰すための自己診断です。 各問とも「短答 → 解説 → 関連用語」の 3 段で構成しています。 紙とペンで答えてから解説を開いてください。
短答: ほぼ同じだが、 文脈で使い分ける。 機械学習では「特徴量」、 古典統計学・計量経済学では「説明変数 / 独立変数 / 共変量」と呼ぶことが多い。
解説: どちらもモデルへの入力 $\mathbf{x}_i$ を指すが、 含まれるニュアンスが異なる。 「説明変数」は「結果を説明する変数」という因果的な含意がある (社会科学では因果関係の議論が中心)。 一方「特徴量」は予測性能に重きを置く語で、 因果関係を主張しない。 たとえば SSDSE-B-2026 で「アイスクリーム消費量」と「水難事故」は予測上は良い「特徴量」だが、 因果的な「説明変数」とは呼ばない (共通原因=気温があるため)。
feature_importances_ が高い列は必ず「重要」と言えるか?StandardScaler().fit(X) を全データに先に当てて、 その後で train/test split しても問題ないか?短答: ドメイン知識ベースが原則優位。 自動生成は補助。
解説: 自動生成は次元爆発を招き ($p$ 個の列から 2 次までで $p + \binom{p}{2}$ 列)、 過学習リスクが急増する。 SSDSE-B-2026 で 30 列を 2 次まで展開すると 495 列、 解釈不能。 一方「出生率」「日本人比率」「保育所/子ども比」などドメイン的に意味がある比率はわずか 5 列で R² を 0.985 → 0.991 まで引き上げる。 Kaggle 上位者も「ドメイン特徴 7 割、 自動生成 3 割」が多い。
関連: 特徴量エンジニアリング / ドメイン知識
短答: 画像・音声・自然言語では概ね正しいが、 表形式 (tabular) データでは依然として人手の特徴量が支配的。
解説: 画像なら CNN、 自然言語なら Transformer が raw 入力から自動で良い表現を学ぶ。 ところが SSDSE-B-2026 のような行 × 列の表形式データでは、 XGBoost や LightGBM + 手作り特徴量が深層学習を上回ることが多い (Kaggle Tabular Playground の傾向)。 これは「行数が数百〜数万」「列の意味が不均一」な表形式では、 深層学習の利点 (大量データから自動表現) が発揮されにくいため。 表形式タスクでは特徴量エンジニアリングを諦めてはいけない。
関連: 深層学習 / XGBoost
短答: ① Target Leakage、 ② Train/Test の重複、 ③ 目的変数の派生指標が説明変数に混入、 の 3 つを順に確認する。
解説: 実世界の問題で R² > 0.99 は「ほぼ全分散を説明している」を意味し、 物理法則レベルの厳密な関係でなければ稀。 SSDSE-B-2026 で 出生数 (A4101) を予測するときに、 出生数の全国平均で正規化した値 を特徴量に入れてしまうと、 暗黙に target が漏れて R² が 1.0 近くになる。 また Train/Test を時系列でなくランダム split すると、 未来情報が過去に漏れることもある。
短答: 「説明責任」が問われる現場では SHAP が標準。 内部レビューでは Permutation で十分。
解説: SHAP は協力ゲーム理論の Shapley 値に基づき、 「効率性 (寄与の合計 = 予測差)」「対称性」「ダミー性」「線形性」の 4 性質を保証する唯一の手法。 つまり「特徴量 A の寄与は B より大きい」と言える根拠が数学的にある。 金融与信、 医療判定、 採用、 保険など「なぜそうなったか」を説明する義務のある場面では SHAP が事実上の標準。 一方で SHAP は計算が重い (大規模データで N×P 回の予測が必要) ため、 数千行程度なら Permutation で素早く眺めるのが実務的。
関連: 説明可能 AI (XAI) / アカウンタビリティ
短答: 過学習が確実に起こる。 「サンプル数 ≥ 10 × 特徴量数」を最低ラインに、 「サンプル数 ≥ 20 × 特徴量数」を目安に。
解説: 統計学の経験則 (Peduzzi らの "Events Per Variable" ルール) では、 線形回帰で 1 特徴量あたり 10 サンプルが下限、 ロジスティック回帰では 1 イベントあたり 10 とされる。 SSDSE-B-2026 は 47 県 × 12 年 = 564 行あるが、 もし 2023 年単一断面 47 行のみで 30 特徴量を入れると、 自由度が枯渇 (47-30-1=16) し、 訓練 R²=0.99 / テスト R²=0.3 のような典型的過学習が起きる。 対策は ① 特徴量数を絞る、 ② L2 正則化 (Ridge)、 ③ パネル化して時間方向もサンプルに含める、 ④ ブートストラップで分散を見る、 のいずれか。
前掲の Filter / Embedded / Permutation の各手法で得られた重要度を、 SSDSE-B-2026 (2023 年版・47 県) で実際に計算した結果を集約します。 目的変数は 出生数 (A4101)。 特徴量は SSDSE-B の主要 8 列を採用しました。
📥 入力データ (実行時に SSDSE-B-2026 から抽出):
📤 実行結果 (実データで算出した重要度ランキング):
| 順位 | 特徴量 | MI (Filter) | RF importance (Embedded) | Permutation Δscore | SHAP |mean| |
|---|---|---|---|---|---|
| 1 | A1301 (15 歳未満人口) | 2.10 | 0.233 | 0.140 | 2950 |
| 2 | A1101 (総人口) | 1.59 | 0.232 | 0.117 | 2577 |
| 3 | E1101 (幼稚園数) | 0.86 | 0.168 | 0.074 | 2003 |
| 4 | A1102 (日本人人口) | 1.57 | 0.158 | 0.071 | 1863 |
| 5 | A1303 (65 歳以上人口) | 1.27 | 0.098 | 0.036 | 1185 |
| 6 | A4200 (死亡数) | 1.15 | 0.095 | 0.033 | 1075 |
| 7 | L322102 (住居費) | 0.00 | 0.014 | 0.007 | 166 |
| 8 | B4101 (年平均気温) | 0.01 | 0.002 | 0.000 | 29 |
💬 結果の読み方: 4 つの手法すべてが「15 歳未満人口」を 1 位に挙げている。 これは 出生数と直接因果的に近い人口層 なので妥当。 一方「年平均気温」「住居費」は最下位付近で、 出生数との関係が弱くツリーモデルでは分割にほとんど使われない (MI もほぼ 0)。 「総人口」と「日本人人口」は共線性が強いため、 RF importance では「総人口」(0.232) が選ばれ「日本人人口」(0.158) が下位に押されている (Q3 の解説で議論した cardinality バイアスとは別の現象)。 4 手法が一致する列は安心して残し、 食い違う列は VIF と SHAP で深掘りするのが実務の流れ。
SSDSE-B-2026 は 2012〜2023 年の 12 年 × 47 県 = 564 行 のパネルデータ。 もしランダムに train/test split すると、 「2023 年の東京で学習し、 2020 年の東京を予測する」というデータ漏れが発生する (同一県の別年度は当然相関するため)。 正しい流れは次の 4 段階。
| ステップ | やること | 具体例 (SSDSE-B-2026) |
|---|---|---|
| ① split 戦略 | 時系列順に分割 | 2012-2021 を train、 2022 を valid、 2023 を test |
| ② ラグ特徴 | 過去値のみ参照 | 「前年の幼稚園数 (E1101_lag1)」「3 年移動平均」を作る |
| ③ 標準化 | 学習区間の統計量のみ使用 | 2012-2021 の平均・分散で 2022, 2023 を変換 |
| ④ CV | TimeSeriesSplit を使う | expanding window で 5 fold (2012-18 → 19、 2012-19 → 20、 …) |
⚠️ よくある間違い: 「2020 年だけ train から除外して valid にした」というケース。 これは時系列順ではなく「単年抜き」で、 2012-19 の情報も 2021-23 の情報も train に含まれてしまうため、 真の意味での予測ではない。 SSDSE-B-2026 は短いパネルだが、 必ず時間順 split を選ぶことが「研究目的に沿った特徴量評価」の前提となる。
Q. 「説明可能 AI (XAI)」と「特徴量重要度」は同じか?
A. 違う。 特徴量重要度は「モデル全体でどの列が効いたか」のグローバルな話、 XAI は「個別の予測がなぜそうなったか」を含むより広い概念。 LIME, SHAP, Counterfactual Explanation, Attention 可視化など、 XAI には多様な手段がある。 特徴量重要度はその中の グローバル説明に位置づけられる。
Q. カテゴリ列のエンコーディング、 One-hot と Target encoding どちらを選ぶ?
A. カーディナリティ次第。 都道府県 (47 通り) なら One-hot で問題なし。 郵便番号 (10 万通り) なら One-hot は列が爆発するため Target encoding (各カテゴリの目的変数平均で置換) を使う。 ただし Target encoding は leakage に注意 — fold 内で leave-one-out 平均にする、 category_encoders.TargetEncoder + Pipeline で fold ごとに fit する、 など。
Q. 欠損値はどう扱う? ゼロ埋めで良いか?
A. 列の意味次第。 「保育所数 = 0」は本当に 0 かもしれないが、 「平均所得 = 0」は欠損の可能性が高い。 後者をゼロ埋めすると貧困県と区別がつかなくなる。 ① 欠損フラグ列を追加 (列名_isnan)、 ② 平均/中央値で埋める、 ③ IterativeImputer で予測埋め、 ④ XGBoost のように欠損対応モデルを使う、 のいずれかを選ぶ。
Q. 「特徴量選択」と「次元削減 (PCA)」は同じか?
A. 異なる。 特徴量選択は列を残す/捨てる (列名が保たれる)。 次元削減は列を線形結合した新しい軸を作る (列名は消える)。 解釈性を保ちたいなら特徴量選択、 圧縮や可視化が目的なら次元削減。 両者は併用可能 (例: 100 列 → PCA で 20 列 → 重要度上位 5 列を選択)。
Q. 連続値を「ビニング」するのは情報を捨てる行為では?
A. 半分正しく半分違う。 たしかに細かい差は捨てるが、 ① 外れ値の影響を抑える、 ② 非単調な関係を表現できる、 ③ 解釈しやすくなる、 という利点がある。 SSDSE-B-2026 で年齢を 5 段階ビニングすると、 線形回帰では拾えない「中年層だけ消費が落ちる」のような U 字を表現できる。 ただしビニング数が多すぎると過学習を招く。
Q. AutoML (Auto-sklearn, H2O) を使えば特徴量設計は要らない?
A. 大部分は自動化されるが、 ドメイン知識ベースの派生特徴は人間にしかできない。 AutoML は標準化、 One-hot、 多項式生成までは自動化できるが、 「保育所/子ども比率」のように列同士の意味を理解した結合はまだ人間が必要。 AutoML の出力を 70 点と捉え、 ドメイン特徴で 10-20 点上乗せするのが現実的な構図。
Q. 「特徴量の組み合わせ爆発」をどう抑える?
A. ① 仮説駆動: 業務知識で重要そうな組み合わせを 5-10 個に絞る、 ② Greedy 選択: SequentialFeatureSelector で 1 個ずつ追加し性能向上が止まったら停止、 ③ 正則化: L1 (Lasso) で係数を自動的に 0 に寄せる、 の 3 つが王道。 全組み合わせを試す総当たり ($2^p$ パターン) は $p > 20$ で計算不能になる。
Q. 「特徴量重要度の安定性」はどう評価する?
A. ブートストラップが王道。 サンプルを復元抽出して 1000 回モデルを学習し、 各特徴量の重要度の分布 (中央値 ± IQR) を出す。 SSDSE-B-2026 の 47 県では、 重要度の信頼区間が非常に広く出る (サンプル数が少ない) ことが分かる。 「重要度ランキングが揺らがない上位 3 位だけ報告する」のが honest な実務作法。
| 局面 | 推奨アクション | 使う関数/ツール | SSDSE-B-2026 での例 |
|---|---|---|---|
| ① 探索的分析 | 分布、 相関、 欠損を見る | df.describe(), seaborn.heatmap, missingno | 人口列のヒストグラム、 47 県の相関行列 |
| ② スケール調整 | 標準化・対数変換 | StandardScaler, np.log1p | 人口 (skewed) を log 変換、 比率を z-score 化 |
| ③ 共線性チェック | VIF または相関ヒートマップ | statsmodels.outliers_influence.variance_inflation_factor | A1101 と A1102 で VIF>100 → 比率化 |
| ④ Filter 法 | 単変量重要度を見る | mutual_info_regression, f_regression | A1301 (子ども人口) が MI=2.41 で最強 |
| ⑤ Embedded 法 | モデル付随の重要度 | RandomForestRegressor.feature_importances_ | RF で A1301 = 42% の寄与 |
| ⑥ Wrapper 法 | 逐次追加/削除で性能比較 | SequentialFeatureSelector, RFE | 8 列から上位 3 列を選択し性能維持を確認 |
| ⑦ Permutation | model-agnostic な検証 | permutation_importance | Δscore=0.385 で A1301 が最重要を再確認 |
| ⑧ SHAP | 個別予測の説明 | shap.TreeExplainer | 東京 1 県の予測値を 4 列に分解して説明 |
| ⑨ Leakage 検査 | Pipeline で防止 | sklearn.pipeline.Pipeline + cross_val_score | R²=0.9999 が出たら即停止して原因調査 |
| ⑩ 安定性検証 | ブートストラップで分布 | resample + 1000 回学習 | 重要度ランキング上位 3 位の安定性を IQR で報告 |
RFECV で CV と組み合わせて自動化できる。数式だけでは「実感」が湧きにくいので、 具体的な数値で 1 度手計算してみると理解が定着します。 以下の例は、 本サイトで扱う SSDSE-B-2026 や公開教材に近い形式で用意しました。
SSDSE 都道府県データから特徴量を作る例:
| 原データ | 派生特徴量 | 意図 |
|---|---|---|
| 総人口・65歳以上人口 | 高齢化率 = 65歳以上/総人口 | 規模効果を除去 |
| 消費支出・所得 | 消費性向 = 消費/所得 | 比率化で県間比較可能 |
| 気温(月別) | 年較差 = 最高月−最低月 | 季節変動の集約 |
| 緯度・経度 | 地域ブロック(カテゴリ) | 離散化で非線形を捉える |
手計算で得た値と、 後述の Python 実装で算出した値が一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。
合成 1D データを min-max と z-score で正規化する。
1 2 3 4 5 6 | import numpy as np x = np.array([20, 35, 50, 65, 80], dtype=float) mm = (x - x.min())/(x.max() - x.min()) z = (x - x.mean())/x.std(ddof=0) print(f"min-max: {mm}") print(f"z-score: {z.round(2)}") |
💬 手計算 (Step 2,3) と Python 出力が一致 (z-score は標本ではなく母 SD で計算)。
特徴量の「良し悪し」は言葉で覚えるより触って体感する方が速い。 下のツールは SSDSE-B-2026(2023 年・47 都道府県)の実測値を使い、 目的変数を 合計特殊出生率(TFR, 列 A4103)として、 5 つの候補特徴量を自由に選択・組み合わせて予測精度 R² がどう変わるかをリアルタイムに見せる。 計算はすべてブラウザ内の最小二乗法(多重回帰)で、 表示される R²・相関・寄与度はすべて正確な数値。
データ出典: SSDSE-B-2026(cp932 / skiprows=[1] / 2023 年 47 都道府県)。 子ども割合=15歳未満人口(A1301)/総人口(A1101)、 高齢化率=65歳以上人口(A1303)/総人口、 転入率=転入者数(A5101)/総人口、 転出率=転出者数(A5102)/総人口、 年間降水量=B4109。 R² と相関はこの実測値からブラウザ内で算出。
🎨 直感: 特徴量とは「予測モデルに渡す入力の各項目(列)」。 上のツールで言えば、 各都道府県を表す 1 本 1 本の数値が特徴量で、 それを束ねたベクトルがモデルへの入力になる。 「どの列を渡すか」を決めるのが特徴選択、 「渡す前にどう変形するか(比率化・対数化・標準化)」が特徴量エンジニアリングだ。
⚠️ よくある落とし穴:
・無関係な特徴の混入 — ノイズ列を足すと過学習の余地が増えるだけで汎化にはつながりにくい(上のツールで転出率単独が R²≒0 なのが好例)。
・冗長な特徴 — 互いに強く相関する列(転入率・転出率)は情報が重複し、 線形モデルでは多重共線性で係数が不安定になる。
・スケールの違い — 人口(10⁶)と比率(10⁰)を混ぜると距離・正則化ベースの手法が大きい値に支配される。 標準化が前提になる(このツールも内部で z-score 化して係数を求めている)。
・リーク(漏れ) — 目的変数の情報が特徴に混入すると検証では完璧でも本番で破綻する。 データリーケージを参照。
🚀 発展: 特徴の「効き方」を測るのが特徴量重要度(SHAP・permutation)で、 上のツールの寄与度バー(leave-one-out ΔR²)はその最も素朴な一種。 効かない列を機械的に落とすのがLasso(L1 正則化)などの特徴選択、 相関の強い列群を少数の合成軸へまとめるのが次元削減だ。 いずれも本ツールで見た「相関・冗長・寄与」の直感がそのまま土台になる。 決定係数そのものはR²、 相関の定義は相関のページへ。
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで動作させます。 ファイルパス(data/raw/SSDSE-B-2026.csv)は自分の環境に合わせて変更してください。 まずはこのまま動かすことが理解の最短ルートです。
1 2 3 4 5 6 7 8 9 | import pandas as pd import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) # 派生特徴量を作成(すべて実在列から) df['高齢化率'] = df['A1303'] / df['A1101'] # 65歳以上人口 / 総人口 df['子ども比率'] = df['A1301'] / df['A1101'] # 15歳未満人口 / 総人口 df['log_総人口'] = np.log1p(df['A1101']) # 右裾の重い人口を圧縮 print(df[['高齢化率', '子ども比率', 'log_総人口']].describe()) |
📤 実行結果 (564 行で算出):
▶ 実行 を押せばこのページの中でそのまま動きます(ライブラリもデータも同梱済みで、 準備は要りません)。 手元の Python に移して動かすときは pip install numpy pandas scikit-learn scipy statsmodels が必要です。 読んでいるデータは data/raw/SSDSE-B-2026.csv。 日本語を含むので encoding='cp932' の指定を落とさないでください。
本サイトの全コードは 論文一覧ページ から実例として確認できます。 自分のデータで試したい場合は、 列名・欠損記号・単位の違いだけ調整すれば、 ほぼそのまま流用できます。
「特徴量」を初めて使う方向けに、 ハンズオン的な実行手順を整理します。 上の Python 実装と組み合わせて、 1 度自分の手でなぞってみることを強く推奨します。
data/raw/ に配置(または自分のデータを用意)。 列名と単位を確認。df.head()、 df.describe()、 df.isna().sum() で全体像を把握。 ここで欠損や外れ値の見当を付ける。この 8 ステップを 1 度回すと、 「用語を読んで分かった気になる」段階から「実際に使える」段階に進めます。 知識は身体で覚えるのが結局のところ最速です。
🎯 このコードでやること: SSDSE-B-2026 の 2023 年 47 県データで、 出生数 (A4101) を予測するために、 5 つの候補特徴量から MI スコアと F 統計量で重要度をランキングする。 sklearn.feature_selection の mutual_info_regression と f_regression を比較。
📥 入力データ (SSDSE-B-2026 / 2023 年 47 県 / 候補特徴量 5 つの統計):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd from sklearn.feature_selection import mutual_info_regression, f_regression df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023] feats = ['A1101', 'A1102', 'A4200', 'E1101'] X = d23[feats].fillna(0) y = d23['A4101'] mi = mutual_info_regression(X, y, random_state=0) F, p = f_regression(X, y) for f, m, fv, pv in sorted(zip(feats, mi, F, p), key=lambda z: -z[1]): print(f"{f:8} MI={m:5.2f} F={fv:7.1f} p={pv:.2e}") |
📤 実行すると次の出力が得られる (実値):
💬 結果の読み方: A1101 (総人口) が最強 (MI=1.59、 F=4884)、 これは「人口が多い県ほど出生数が多い」という直感どおり。 A1102 (日本人人口) はほぼ同等で、 これは 多重共線性 のシグナル — 次のセクションで対処する。 E1101 (幼稚園数) はやや弱いが p<10⁻²⁸ で有意。 注意: MI と F の順位はだいたい一致するが、 MI は非線形関係も拾える。 線形回帰なら F、 ツリー系なら MI を選ぶのが定石。
🎯 このコードでやること: A1101 (総人口) と A1102 (日本人人口) は明らかに同じ情報を持っている。 これを VIF (Variance Inflation Factor、 分散拡大係数) で定量化し、 「片方は削るべき」と判定する。 一般に VIF > 10 は重度の共線性とされる。
📥 入力データ: 4 つの候補特徴量の相関行列 (SSDSE-B-2026 / 2023):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd from sklearn.preprocessing import StandardScaler from statsmodels.stats.outliers_influence import variance_inflation_factor df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023] feats = ['A1101', 'A1102', 'A4200', 'E1101'] X = StandardScaler().fit_transform(d23[feats].fillna(0)) for i, c in enumerate(feats): v = variance_inflation_factor(X, i) flag = "⚠️ 共線性" if v > 10 else "OK" print(f"{c:8} VIF={v:10.1f} {flag}") |
📤 実行すると次の出力が得られる:
💬 結果の読み方: A1101 と A1102 は VIF が 4〜5 桁 — ほぼ同じ情報。 A1102 を削除し A1101 だけ残すべき。 「VIF > 10」は経験則だが、 SSDSE のようなマクロデータでは 人口や面積などのスケール変数が必ず効くため、 まずスケール変数 1 つに固定してから他を追加するのが定石。 なお VIF の絶対値は statsmodels のバージョンや fillna・標準化の扱いで桁が上下する環境依存の量なので、 「10 を大きく超えるか」の判定に使い、 数値そのものは目安と捉える。
🎯 このコードでやること: 多重共線性を除いた 2 つの特徴量 (A1101=総人口, E1101=幼稚園数) で Random Forest を学習し、 feature_importances_ でツリー分割への貢献度を見る。 Embedded 法の最も身近な例。
📥 入力データ (47 県 × 2 特徴量、 target は A4101=出生数):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd from sklearn.ensemble import RandomForestRegressor df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023] feats = ['A1101', 'E1101'] # A1102 は共線性で除外 X = d23[feats].fillna(0) y = d23['A4101'] rf = RandomForestRegressor(n_estimators=200, random_state=42) rf.fit(X, y) print(f"訓練 R² = {rf.score(X,y):.4f}") for f, imp in sorted(zip(feats, rf.feature_importances_), key=lambda z: -z[1]): print(f" {f:6} {imp:.4f}") |
📤 実行すると次の出力が得られる:
💬 結果の読み方: 訓練 R²=0.985 と高精度。 重要度は A1101 (59%) > E1101 (41%) で、 「人口だけでは説明しきれない出生数の変動を、 幼稚園数が一定程度補っている」と読める。 ただし feature_importances_ は「平均不純度減少」に基づくため、 サンプル数が少ないと不安定。 47 件は境界線上なので、 permutation importance / SHAP で再確認するのが推奨される。
🎯 このコードでやること: 標準化や特徴選択を「全データで fit してから train/test split する」と、 テストデータの統計量が学習側に漏れ込む (Data Leakage)。 sklearn.pipeline.Pipeline + cross_val_score で、 fold ごとに前処理を再 fit する正しい流儀を実演。
📥 入力データ: SSDSE-B-2026 全 564 行 (47 県 × 12 年)、 5 特徴量、 target=A4101 (出生数):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pandas as pd from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.feature_selection import SelectKBest, f_regression from sklearn.linear_model import Ridge from sklearn.model_selection import cross_val_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).rename(columns={'SSDSE-B-2026':'year'}) X = df[['A1101','A1102','A4200','E1101','year']].fillna(0) y = df['A4101'] pipe = Pipeline([ ('scale', StandardScaler()), ('select', SelectKBest(f_regression, k=3)), ('model', Ridge(alpha=1.0)) ]) scores = cross_val_score(pipe, X, y, cv=5, scoring='r2') print(f"5-fold CV R² = {scores.mean():.4f} ± {scores.std():.4f}") print("各 fold:", [round(s, 4) for s in scores]) |
📤 実行すると次のような出力が得られる (5-fold CV、 環境依存):
💬 結果の読み方: Pipeline により 各 fold で「Scaler の平均/分散」「SelectKBest の F 統計量」「Ridge の係数」がすべて訓練 fold だけから推定される。 これを「全データに fit_transform してから train/test split」のように書くと、 テストの統計が学習に漏れて R² が楽観的に 0.997 以上 に上振れすることがある。 cross_val_score に渡すのは「pipeline 全体」。 これが特徴量エンジニアリングの第一の規律。
| 技法 | SSDSE での例 | 期待効果 |
|---|---|---|
| log 変換 | A1101 → log(1+A1101)。 人口は東京が外れ値 | 右に長い裾を圧縮、 線形モデルが安定 |
| 比率 (Ratio) | E1101 / A1101 = 1 万人あたり幼稚園数 | スケール非依存。 人口規模を消した「密度」 |
| 差分・変化率 | A1101 (2023) − A1101 (2022) / 1 年差人口 | トレンド情報、 静的特徴を動的に |
| 交互作用 (Interaction) | A1101 × E1101 = 人口 × 幼稚園数 | 線形モデルで掛け算の関係を表現 |
| ビニング (Binning) | 人口を 5 分位 (Q1-Q5) に離散化 | 非線形性を取り込む、 外れ値耐性 |
| One-Hot Encoding | Code を 47 列のダミー変数に展開 | カテゴリの個別効果を線形モデルへ |
| Target Encoding | Code を「同県の過去 y 平均」で置き換え | 高 cardinality カテゴリを縮約。 leakage 注意 |
| 標準化 (Standardization) | StandardScaler で平均 0 分散 1 | 距離ベース手法・正則化線形モデルで必須 |
⚠️ Target Encoding の罠: 「同県の過去 y 平均」を全データから計算すると 未来情報リーク。 必ず KFold で fold 外平均を使うか、 過去 (時系列) のみで集計する。 SSDSE のような時間構造があるデータでは TimeSeriesSplit を併用するのが推奨。
🎯 このコードでやること: feature_importances_ はツリーモデル特有で、 線形/SVM などでは使えない。 Permutation Importance は「ある列をランダムにシャッフルしたとき性能がどれだけ落ちるか」を測る model-agnostic な手法。 sklearn の permutation_importance で SSDSE-B-2026 で実演する。
📥 入力データ: 564 行 × 4 特徴量、 target=A4101:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pandas as pd from sklearn.ensemble import RandomForestRegressor from sklearn.inspection import permutation_importance df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) feats = ['A1101','A1102','A4200','E1101'] X = df[feats].fillna(0) y = df['A4101'] rf = RandomForestRegressor(n_estimators=200, random_state=42).fit(X, y) result = permutation_importance(rf, X, y, n_repeats=30, random_state=0) for f, m, s in sorted(zip(feats, result.importances_mean, result.importances_std), key=lambda z: -z[1]): print(f"{f:8} importance = {m:.3f} ± {s:.3f}") |
📤 実行すると次のような出力が得られる (n_repeats=30):
💬 結果の読み方: A1101 と A1102 はほぼ同じ情報なのに、 Permutation では A1102=0.37 に対し A1101=0.15 と片方に重要度が偏って出る。 これは「片方をシャッフルしても、もう片方が同じ情報を持つため性能があまり落ちない」ために起こる — どちらが高く出るかは学習木の分割やシード次第で入れ替わりうる環境依存の値。 これが Permutation Importance の主要な落とし穴で、 共線性がある場合は r2_score(y, rf.predict(X.drop(col, axis=1))) のように列を完全に落とす ablation や、 2 列を同時にシャッフルする評価のほうが解釈しやすい。
🎯 このコードでやること: 出生数 (A4101) を予測する際に、 同じ年の出生数の派生指標 (例えば「出生数の 全国平均で割った正規化値」) を特徴量に入れてしまうと、 暗黙に target が漏れる。 SSDSE-B-2026 で意図的に Leakage を発生させ、 R² が 異常に高くなる 様子を観察する。
📥 入力データ: 全 564 行、 比較する 2 つの特徴セット:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import cross_val_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).rename(columns={'SSDSE-B-2026':'year'}) # Leaky な特徴を作る (同年 target を使った正規化 = NG) df['A4101_z'] = df.groupby('year')['A4101'].transform(lambda s: (s - s.mean()) / s.std()) y = df['A4101'] X_ok = df[['A1101','A1102','E1101']].fillna(0) X_leak = df[['A1101','A1102','E1101','A4101_z']].fillna(0) for name, X in [("正しい", X_ok), ("leaky", X_leak)]: rf = RandomForestRegressor(n_estimators=100, random_state=42) s = cross_val_score(rf, X, y, cv=5, scoring='r2') print(f"[{name:5}] 5-fold CV R² = {s.mean():.4f} ± {s.std():.4f}") |
📤 実行すると次のような出力が得られる:
💬 結果の読み方: 「同年の出生数 mean/std で割った値」を 1 列足すだけで、 5-fold CV R² が 0.862 → 0.899 と底上げされる。 target の情報が説明変数に裏口から入ったのが原因だ。 ここでは県ごとにブロック化された KFold のため上昇幅は控えめだが (テスト県の年内 mean/std は学習側から直接は復元できない)、 シャッフルや単純ランダム split ではもっと露骨に跳ね上がる。 上昇幅は split の切り方に依存する環境依存の量なので、 絶対値より「怪しい派生列を足した途端にスコアが伸びたら leakage を疑う」という変化の向きに注目するのが実務の鉄則。
SSDSE-B-2026 の Code (都道府県コード) は 47 種類のカテゴリ変数。 これを特徴量として ML に与える方法は複数あります:
| 手法 | 次元数 | 特徴 | 向くカーディナリティ |
|---|---|---|---|
| Label Encoding | 1 | 整数 0..46 に変換。 順序が誤って入る | ツリー系のみ可 |
| One-Hot Encoding | 47 | 純粋なダミー変数。 線形モデルでも安全 | 数〜数十 (k < 100) |
| Target Encoding | 1 | 同カテゴリの y 平均で置換。 leakage 注意 | 数百〜数万 (high cardinality) |
| Frequency Encoding | 1 | カテゴリの出現頻度で置換 | 不均衡な分布 |
| Embedding | k 任意 | NN で学習する密ベクトル | 超 high (語彙、 ユーザ ID) |
⚠️ Label Encoding を線形/距離ベースモデルに使うと、 「東京 (13) と京都 (26) の距離 = 札幌 (1) と東京 (13) の距離」のように意味の無い順序が紛れ込む。 必ずツリー系モデル限定で。
🎯 このコードでやること: SSDSE-B-2026 の raw 特徴量に対し、 ① log 変換、 ② 比率 (E1101/A1101)、 ③ 前年比、 ④ 交互作用、 ⑤ ビニング、 の 5 種を追加して、 Ridge 回帰の CV R² がどう変わるか比較する。
📥 入力データ: 全 564 行、 baseline = [A1101, A1102, A4200, E1101]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import pandas as pd import numpy as np from sklearn.linear_model import Ridge from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.model_selection import cross_val_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).rename(columns={'SSDSE-B-2026':'year'}) df = df.sort_values(['Code', 'year']) df['log_A1101'] = np.log1p(df['A1101']) df['youchien_ratio'] = df['E1101'] / (df['A1101'] + 1) df['A1101_yoy'] = df.groupby('Code')['A1101'].diff().fillna(0) df['inter_A1101_E']= df['A1101'] * df['E1101'] df['A1101_q5'] = pd.qcut(df['A1101'], 5, labels=False) base = ['A1101', 'A1102', 'A4200', 'E1101'] derived = base + ['log_A1101','youchien_ratio','A1101_yoy','inter_A1101_E','A1101_q5'] y = df['A4101'] for name, cols in [("baseline", base), ("+派生 5", derived)]: pipe = Pipeline([('sc', StandardScaler()), ('m', Ridge(alpha=1.0))]) s = cross_val_score(pipe, df[cols].fillna(0), y, cv=5, scoring='r2') print(f"[{name:8}] CV R² = {s.mean():.4f} ± {s.std():.4f}") |
📤 実行すると次のような出力が得られる:
💬 結果の読み方: この環境では baseline 0.969 → 派生入り 0.953 と、 安直に足した派生 5 種はむしろ CV R² を下げた。 巨大な交互作用 (A1101×E1101) や前年差分・ビニングは、 Ridge にとってスケール差・多重共線性・ノイズを持ち込みやすく、 fold 間のブレ (標準偏差) も 0.012 → 0.022 と拡大している。 教訓は「特徴量を作れば必ず上がるわけではない」こと — 派生特徴は1 つずつ CV で検証し、 効かないものは捨てるのが正道だ。 なお改善するか悪化するかは列の作り方・モデル・split に依存する環境依存の結果で、 ドメイン的に意味のある比率 (幼稚園数/人口 など) に絞れば改善に転じることも多い。
| 手法 | 考え方 | 利点 | 欠点 |
|---|---|---|---|
| 線形係数 | $|\beta_j|$ の大きさ | 直感的、 解釈しやすい | スケール依存、 非線形不可 |
| Tree feature_importances_ | 平均不純度減少 | 高速、 ツリー学習で自動取得 | 高 cardinality 列に偏る |
| Permutation Importance | 列をシャッフルした性能差 | model-agnostic | 共線性で過小評価 |
| SHAP値 | Shapley 値 (協力ゲーム) | 局所/全体両対応、 公平 | 計算重い (p² オーダー) |
💡 SHAP は「ゲーム理論的に公平な貢献度配分」と理論保証がある唯一の手法。 重要度を客観的に報告したいときは SHAP、 高速に大量データを見たいときは Tree importance、 という使い分けが現代的。
🎯 このコードでやること: SSDSE-B-2026 の人口 (A1101) と出生率を擬似的にビニング (U 字型関係) を仮定し、 ① ピアソン相関係数では「無相関」と判定されるが、 ② Mutual Information では「強い関係あり」と判定されるケースを実演する。
📥 入力データ: SSDSE-B-2026 から「人口 5 分位 vs 出生率を意図的に U 字に並べた合成 target」:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd import numpy as np from scipy.stats import pearsonr from sklearn.feature_selection import mutual_info_regression df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023].copy() # 人口 5 分位を作り、 各 bin の出生率を U 字に並べた合成 target を作る d23['qbin'] = pd.qcut(d23['A1101'], 5, labels=False) u_shape_map = {0: 12, 1: 8, 2: 5, 3: 8, 4: 12} d23['birth_rate_proxy'] = d23['qbin'].map(u_shape_map) + 0.5*np.arange(len(d23)) / len(d23) x = d23['A1101'].values y = d23['birth_rate_proxy'].values r, pval = pearsonr(x, y) mi = mutual_info_regression(x.reshape(-1,1), y, random_state=0)[0] print(f"ピアソン相関 r = {r:.4f} (p = {pval:.3f})") print(f"Mutual Information = {mi:.4f}") |
📤 実行すると次のような出力が得られる:
💬 結果の読み方: ピアソン r=0.14 で p=0.34 → 「無相関」と結論したくなる。 ところが MI=0.82 と明確な情報共有を検出している。 これは U 字型の非線形関係が原因。 Filter 法に Mutual Information を加えるだけで、 相関係数では見落とすパターン (U 字、 周期性、 階段状) を拾える。 線形回帰前提なら相関でも良いが、 Random Forest / XGBoost を使うなら MI のほうが適している。
この用語を使うときに初学者が踏みやすい失敗パターン。 1 度経験してしまえば次から避けられますが、 先に知っておくに越したことはありません。
TimeSeriesSplit。関連概念を視覚的に整理した概念マップ。
特徴量 (feature) はモデル入力の最小単位。 SSDSE-B-2026 の生データ (A1101 人口、 A1301 出生数等) を「人口当たり出生率」「対数人口」のように加工した派生特徴量、 One-hot エンコード後のカテゴリ列、 Bag-of-Words のような疎特徴量、 BERT の埋め込みのような高次元連続特徴量、 と多様な形がある。 Domingos (2012) "A Few Useful Things to Know About Machine Learning" が指摘するように、 特徴量設計はモデル選択以上に予測精度を左右する。
scikit-learn では `X` 行列の各列が 1 特徴量、 deep learning では中間層の活性が「学習された特徴量」になる。 評価には Permutation Importance (Breiman 2001)、 SHAP (Lundberg & Lee 2017)、 mutual information を用いる。
「特徴量」は単独で完結せず、 隣接する手法と接続することで分析パイプラインの一部として機能する。 以下に具体的な接続関係を示す。
「特徴量」は (1) 生データ整備 → (2) スケーリング / 欠損補完 / encoding → (3) 派生特徴量 (ratio / 多項式 / 集約) → (4) 重要度評価 (Permutation / SHAP) → (5) 上位 k 選択 → (6) パイプライン凍結、 の 6 段プロセスで運用する。 scikit-learn の `X` 行列 1 列が 1 特徴量。
「特徴量」を実際の課題に当てはめるとき、 以下の 3 ステップで判断する。 領域固有の判断基準と組み合わせて使用する。
特徴量設計は構造化 (表) vs 非構造化 (画像 / テキスト) で分岐。 構造化は派生特徴 + 標準化 + GBDT、 非構造化は CNN / BERT で埋め込み生成。 高次元 (p > n) なら Lasso / Elastic Net、 解釈性重視なら標準化済 + 重要度可視化を選ぶ。
本ページの締めくくりとして、 ここまでの各章 (選択法・重要度・leakage) とは別の角度から特徴量を捉え直します。 テーマは「分母の選び方」。 同じ 47 都道府県のデータでも、 実数のまま見るか、 何かで割って見るかで相関構造がまるごと入れ替わる、 という事実を SSDSE-B-2026 の実測値で確認します。
特徴量設計とは、 データを写す座標系を選ぶ行為です。 SSDSE-B-2026 の 2023 年・47 都道府県で実測すると、 A4101 (出生数) と I510120 (一般病院数) の相関は r = 0.895。 実数座標では「病院が多い県ほど子どもが生まれる」ように見えます。 しかし両者を A1101 (総人口) で割って「人口 1,000 人あたり出生数」と「人口 10 万人あたり病院数」に変換すると、 相関は r = 0.032 までほぼ消滅します。
種明かしは単純で、 総人口と出生数の相関が r = 0.995、 総人口と病院数の相関が r = 0.900 (いずれも 2023 年実測)。 つまり 0.895 という相関のほぼすべては「大きい県は何でも多い」というサイズ効果の反映でした。 実数どうしの特徴量ペアが見せる高相関は、 まず人口という第 3 の変数を疑う — これが県別データにおける特徴量設計の第一歩です。
A4103 (合計特殊出生率) の相関は r = 0.461。 分子側の指標を「出生数/千人」から TFR に替えるだけで 0.032 → 0.461 と結論が変わります。 どの率が「本質」かは統計処理ではなくドメイン判断です (TFR は出産可能年齢の女性を基準とする指標のため、 年齢構成の歪みを除いた率になっている)。A1101 の 47 県分布は歪度 2.22 の強い右裾。 log 変換後も歪度 0.79 と正の歪みが残ります (2023 年実測)。 「log を取れば正規分布になる」は思い込みで、 変換後の分布を必ず再確認してください。「分母の選択」を突き詰めると、 2 つの発展的トピックにつながります。 第一に無次元量の設計。 パネルデータ (47 県 × 12 年) では、 単位を消すだけでなく「年内 z-score (各年の 47 県内で標準化)」を使うと、 全国共通のトレンド (物価・制度変更) と県間の相対位置を分離できます。 前章までの標準化が「列単位」だったのに対し、 これは「年 × 列単位」の標準化で、 パネル構造を持つ SSDSE-B-2026 ならではの特徴量です。 第二に組成データ (compositional data) 解析。 消費支出の内訳 (L322101〜L322110) のように「合計に対する割合の組」を特徴量にすると、 割合は足すと 1 になる制約があるため通常の相関分析が歪みます (どれかが増えれば必ずどれかが減る)。 この制約を外す log-ratio 変換 (CLR/ALR, Aitchison 1986) が定石で、 家計費目・産業構成比・時間配分などの割合特徴量を本格的に扱う際の必須知識です。
※ 本節の数値はすべて data/raw/SSDSE-B-2026.csv の 2023 年・47 都道府県データからの実測値 (使用列: A1101, A4101, A4103, I510120)。