本ページでは、 データエンジニアリングを統合的に解説します。 ETL/ELT・データクレンジング・結合・集約・欠損補完・エンコーディング・スケーリング・パイプラインを一気通貫で扱います。
「分析の 80% は前処理」と言われます。 SSDSE-B のような綺麗な統計データでも、 結合・型変換・欠損処理が必要です。 ここでは pandas を中心に実務で使う技を整理します。
論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:
データエンジニアリングは 「料理の下ごしらえ」 です。 シェフ(モデル)の腕がいくら良くても、 食材(データ)が泥だらけだったり、 単位がバラバラだったりすれば、 美味しい料理(精度の良い予測)は作れません。 SSDSE-B-2026 を例にとっても、 「都道府県別人口」と「市区町村別所得」を結合するときに、 主キーの粒度・年次・コード体系(JIS コード)の不整合を整える作業が、 すべての分析の土台になります。
具体的には ETL(Extract → Transform → Load) のサイクルでデータを流通させます。 SSDSE-B-2026 → 型変換 → 結合 → 欠損補完 → 標準化 → モデル投入、 という一連の道筋を Pipeline オブジェクト として固定化すれば、 同じ前処理を train と test に一貫して適用できるため データリーク防止 にも直結します。
代表的な前処理は以下のとおり数学的に厳密に書けます。
$$ \text{Standardize: } z_i = \frac{x_i - \mu_x}{\sigma_x}, \quad \text{Min-Max: } x'_i = \frac{x_i - x_{\min}}{x_{\max} - x_{\min}}, \quad \text{Robust: } r_i = \frac{x_i - \text{median}(x)}{\text{IQR}(x)} $$SSDSE-B-2026 で「総人口(A1101)」を z スコア化することは、 K-means や SVM のような距離・勾配ベース手法で 必須 です。 数式に従って $\mu$ と $\sigma$ を train で計算、 同じ値を test に適用するのが鉄則です。
| 記号 | 意味 | SSDSE-B-2026 での例 |
|---|---|---|
| $\mu_x$ | 学習データでの平均 | A1101(人口)の 47 都道府県平均(約 270 万人) |
| $\sigma_x$ | 学習データでの標準偏差 | A1101 の SD(東京の極大により大きめ) |
| IQR | 第 1 ・第 3 四分位の差 | 外れ値の影響を受けにくいスケール尺度 |
数式の意味は「平均から何 SD 離れているか(z)」「最小最大の間でどの位置か(Min-Max)」「中央値と IQR で測ったロバストな位置(Robust)」。 用途で使い分けます。
SSDSE-B-2026 の A1101(総人口)について、 3 つの正規化を 47 都道府県の最小・中央値・最大の代表 3 県(鳥取県・岐阜県・東京都)で電卓レベルで追ってみます。 平均 μ ≈ 2,645,787 人、 標準偏差 σ ≈ 2,998,310 人、 最小 = 547,000 人(鳥取)、 中央値 ≈ 1,899,000 人(岐阜)、 最大 = 13,921,000 人(東京)、 IQR ≈ 1,950,000 人とします。
| 県 | 原値 | z-score | Min-Max | Robust (IQR) |
|---|---|---|---|---|
| 鳥取県 | 547,000 | −0.70 | 0.00 | −0.69 |
| 岐阜県 | 1,899,000 | −0.25 | 0.10 | 0.00 |
| 東京都 | 13,921,000 | +3.76 | 1.00 | +6.17 |
z-score では東京が +3.76σ という強い外れ値だが、 Min-Max は東京を 1.0、 鳥取を 0.0 に張り付ける(中央値の岐阜は 0.10 で右寄り)。 Robust スケーリングでは東京が +6 を超え、 外れ値性がより強調されます。 線形回帰では z-score、 木モデルでは Min-Max、 外れ値検出では Robust と使い分けます。
StandardScaler / MinMaxScaler / RobustScaler を SSDSE-B-2026 の A1101 に適用して、 上の表と同じ数値が出ることを確認する。data/raw/SSDSE-B-2026.csv の A1101 列(47 行)。 各 Scaler に渡すには 2 次元化([[...]])が必須。import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
x = df[['A1101']]
print('z-score (mean=0, std=1):',
StandardScaler().fit_transform(x).flatten()[:3].round(2))
print('min-max [0,1] :',
MinMaxScaler().fit_transform(x).flatten()[:3].round(2))
print('robust (median, IQR) :',
RobustScaler().fit_transform(x).flatten()[:3].round(2))
分析・モデリングに使える形にデータを整える・繋ぐ・流す工程。 データサイエンスの 70-80% の工数がここに費やされる。
本ページは分析者寄りの DE 知識をまとめます。
ColumnTransformer + Pipeline で「数値列:中央値補完 → 標準化」「カテゴリ列:one-hot エンコード」を学習データのみで fit し、 リーク無しで線形回帰の R2 を評価する一連の流れを 1 つのパイプにする。data/raw/SSDSE-B-2026.csv (CP932、 2 行目スキップ)。 説明変数:A1101(総人口)・A4101(出生数)・A6101(高齢化率)・地方区分。 目的変数:D1101(医師数)。 train:test = 7:3、 random_state=42。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 28 29 | from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) num_cols = ['A1101', 'A4101', 'A6101'] ## 数値列 cat_cols = ['地方区分'] ## カテゴリ列(事前に作成済の想定) preprocessor = ColumnTransformer([ ('num', Pipeline([ ('impute', SimpleImputer(strategy='median')), ('scale', StandardScaler())]), num_cols), ('cat', OneHotEncoder(handle_unknown='ignore', drop='first'), cat_cols) ]) pipe = Pipeline([ ('prep', preprocessor), ('model', LinearRegression()) ]) X = df[num_cols + cat_cols] y = df['D1101'] ## 例えば「医師数」を目的変数に X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42) pipe.fit(X_tr, y_tr) ## train でだけ統計量を学習 — リーク完全防止 print(f'R² (test): {pipe.score(X_te, y_te):.3f}') |
fit_transform を全体に適用してから分割すると、 R2 が過大評価される典型バグ。df['A1101'](47 都道府県の総人口、 SSDSE-B-2026 由来)。 dropna() で欠損除外。 0.6745 は MAD を正規分布の SD 推定量に揃える係数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from scipy import stats import numpy as np x = df['A1101'].dropna() ## 通常の z-score(外れ値の影響を受ける) z_normal = (x - x.mean()) / x.std() ## ロバスト z-score(MAD ベース・東京の影響を受けにくい) median = x.median() mad = stats.median_abs_deviation(x) z_robust = 0.6745 * (x - median) / mad ## Trimmed mean(上下 10% を除いた平均) trimmed = stats.trim_mean(x, 0.1) ## IQR で外れ値検出 q1, q3 = np.percentile(x, [25, 75]) iqr = q3 - q1 outliers = x[(x 1.5*iqr) | (x > q3 + 1.5*iqr)] print(f'外れ値: {outliers.tolist()}') print(f'通常平均: {x.mean():.0f}, 10%トリム平均: {trimmed:.0f}') |
df[['A1101']] (47 行 × 1 列、 単位:人)。 sklearn の PowerTransformer / QuantileTransformer はいずれも 2 次元入力を要求する点に注意。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from sklearn.preprocessing import PowerTransformer, QuantileTransformer ## Yeo-Johnson 変換:負値もOK、 Box-Cox の拡張 pt = PowerTransformer(method='yeo-johnson') x_transformed = pt.fit_transform(df[['A1101']]) ## Quantile 変換:分位点ベース、 任意分布に強制マッピング qt = QuantileTransformer(output_distribution='normal', n_quantiles=47) x_quantile = qt.fit_transform(df[['A1101']]) ## 歪度の比較 print(f'元の歪度 : {stats.skew(df["A1101"]):.3f}') print(f'log1p 後 : {stats.skew(np.log1p(df["A1101"])):.3f}') print(f'Yeo-Johnson 後 : {stats.skew(x_transformed.flatten()):.3f}') print(f'Quantile 後 : {stats.skew(x_quantile.flatten()):.3f}') |
pt.inverse_transform() が使える。data/raw/SSDSE-B-2026.csv (CP932、 2 行目スキップ)。 利用列:年度・A1101(総人口)・A4101(65歳以上人口)。1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import polars as pl df_pl = pl.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skip_rows_after_header=1) ## pandas より 5-10 倍速い:lazy evaluation で最適化 result = (df_pl .filter(pl.col('年度') == 2023) .with_columns([ (pl.col('A4101') / pl.col('A1101') * 100).alias('aging_rate'), pl.col('A1101').log1p().alias('log_pop') ]) .sort('aging_rate', descending=True) .head(5)) print(result) |