「顧客データに年代が入っていないので推定したい」「アンケート回答の欠損を埋めたい」「購買履歴からライフステージを付けたい」。こうした の相談は多いのですが、手法がバラバラに語られがちです。この記事では目的別に手法を並べ、メリット・デメリットと検証方法を実装付きで整理します。
扱うのは 「1 件ごとの値を埋める・足す」 側です。サンプルから母集団全体を推計する話(拡大推計)は 指標設計の教科書 EP.19 に独立した記事があるので、そちらを参照してください。欠損値そのものの基礎は EP.02、名寄せによる統合は EP.06 で扱っています。
1. リッチ化を3つに分ける
最初にやるべきは手法選びではなく分類です。「値を埋める」と一括りにされているものの中に、検証のしやすさがまったく違う3種類が混ざっています。ここを分けないまま進むと、検証できないものを検証できたつもりで本番に出すことになります。
| 種類 | やっていること | 検証のしやすさ | 例 |
|---|---|---|---|
| ① 決定的な補完 | 対応表を引いて確定させる | 容易。照合できたかどうかで判定できる | 郵便番号 → 都道府県 |
| ② 統計的な補完 | 分布や他変数から推す | 中。人工的に欠損させれば測れる | 年齢を群別中央値で埋める |
| ③ 推論による付与 | 別の情報から当てる | 難しい。そもそも正解が存在しないことが多い | 購買履歴 → ライフステージ |
③ が最も危ういのは、精度が低いからではなく「精度が分からない」からです。年齢の補完なら、一部を隠して当てさせれば誤差が測れます。しかし「この人は子育て世帯か」を購買履歴から推定した場合、答え合わせをする手段が社内にないことが普通です。
必ず決定的な補完から手をつけてください。マスタや対応表で埋まる分を先に埋めると、②③ で扱う残りが減り、しかも埋まった部分は説明可能です。いきなりモデルや に行くと、対応表で確定できたはずの値まで推定値になります。
2. 目的別・手法カタログ
目的から引ける形で並べます。前提条件の列が重要で、ここが満たされていない手法を選ぶと、精度以前に前提が崩れます。
| 目的 | 手法 | 前提 | メリット | デメリット |
|---|---|---|---|---|
| 数値を埋める | 全体の平均・中央値 | 欠測がほぼランダム | 実装が最速 | 分散が縮む。群間差が消える |
| 群別の代表値 | 群と値に関係がある | 構造を活かせ、説明しやすい | 群を細かくすると件数不足に陥る | |
| 回帰による補完 | 他変数と相関がある | 精度が出やすい | 関係が崩れる領域で大きく外す | |
| 多重代入 | 統計的推論をしたい | 不確実性を持ち越せる | 実装・解釈のコストが高い | |
| カテゴリを埋める | 最頻値 | — | 単純 | 少数カテゴリが消滅する |
| 分類モデル | ラベル付きが十分ある | 精度が出せる | 学習データの偏りを再生産する | |
| 「不明」を値として立てる | — | 嘘をつかない | 下流での対応が必要 | |
| 属性を新規付与 | マスタ・辞書の照合 | 対応表が存在する | 決定的で説明可能。最優先 | 網羅率が上限になる |
| 外部データの結合 | 結合キーがある | 情報量が一気に増える | ライセンス・鮮度・粒度の不一致 | |
| 行動からの推定 | 行動と属性に関係がある | 大規模に効く | 相関であって因果ではない | |
| による推定 | 判断材料が文章にある | 実装が速く非定型に強い | 再現性が低い。検証コスト大 | |
| サンプル→全体 | 拡大推計 | 抽出設計が分かる | 全体像が出せる | EP.19 参照 |
3. 欠測の理由を先に考える
手法を選ぶ前に、、つまり「なぜ欠けているのか」を考えます。ここを飛ばすと、統計的に妥当でない補完を平気でやってしまいます。
- 完全にランダムに欠けている: 入力機器の不調など。補完しても偏りは出にくい
- 他の観測値で説明できる欠け方: 「若い層ほど年収を書かない」など。その説明変数を使った補完が有効
- 欠けていること自体が値と関係する: 「収入が高い人ほど答えない」など。この場合、補完はほぼ必ず偏る
欠けていること自体が情報を持っている場合、埋めた瞬間にその情報が消えます。この状況では「欠損フラグを特徴量として残す」方が、下手に推定するより正確です。EP.02 の「フラグ化」がまさにこの選択肢です。
4. 統計的補完を実際に比べる
「群の構造を使うと何が変わるか」を、人工的に欠損を作って実測します。この検証の作り方自体が、この章の要点です。
import numpy as npimport pandas as pd
rng = np.random.default_rng(42)
# 年齢が地域と関係を持つデータを作る(他変数で説明できる欠け方を想定)n = 500region = rng.choice(["都心", "郊外", "地方"], size=n, p=[0.4, 0.35, 0.25])base = {"都心": 34, "郊外": 42, "地方": 51}age = np.array([rng.normal(base[r], 8) for r in region]).round()truth_df = pd.DataFrame({"region": region, "age": age})
# 検証のため「正解が分かっている状態」で人工的に3割を欠損させるmask = rng.random(n) < 0.3observed = truth_df.copy()observed.loc[mask, "age"] = np.nan
truth = truth_df.loc[mask, "age"].to_numpy()
def rmse(pred, actual): return float(np.sqrt(np.mean((pred - actual) ** 2)))
# 手法A: 全体の平均で埋めるpred_a = np.full(int(mask.sum()), observed["age"].mean())
# 手法B: 群(地域)ごとの中央値で埋めるmed = observed.groupby("region")["age"].median()pred_b = observed.loc[mask, "region"].map(med).to_numpy()
print(f"全体平均で補完 : RMSE {rmse(pred_a, truth):.2f}")print(f"地域別中央値で補完 : RMSE {rmse(pred_b, truth):.2f}")
# 補完は分布を痩せさせる。精度とは別に、この副作用も確認しておくprint(f"元データの標準偏差 : {truth_df['age'].std():.2f}")print(f"全体平均で埋めた後の標準偏差 : {observed['age'].fillna(observed['age'].mean()).std():.2f}")2つのことが同時に分かります。群の構造を使うと誤差が下がること、そしてどちらの手法でも標準偏差が縮むことです。後者は精度の話ではなく分布の歪みなので、補完後のデータで分散や相関を語るときには必ず意識が要ります。
5. 推論による属性付与
ここが 3 分類の ③、最も需要が多く、最も検証が難しい領域です。手段は大きく3つあります。
- 行動からの推定: 購買・閲覧・利用時間帯などから属性を当てる。大規模データがあるほど効くが、 を使っているという自覚が要る
- モデルによる分類: 一部にラベルがあるなら学習できる。ラベルの偏りがそのまま推定の偏りになる
- による推定: 自由記述や商品名など、文章から判断できる場合に強い。実装が速い反面、同じ入力で出力が揺れる
LLM を使う場合の実務的な注意を挙げます。出力を自由文にしないこと(選択肢の集合に制約する)、同じ入力を複数回流して一致率を測ること、そして判断材料が本当に入力に含まれているかを確認することです。材料がないのに推定させると、モデルはもっともらしい多数派の値を返してきます。これは推定ではなく、ただの偏りの再生産です。
推定した属性を入力にして、さらに別の属性を推定する。この連鎖は誤差を掛け算で増幅させます。しかも各段で「もっともらしい値」に寄るため、出力は自信ありげなのに実態から離れるという最悪の形になりがちです。推定は1段まで、を原則にしてください。
6. 推定値を下流でどう扱うか
この章がこの記事で最も重要かもしれません。手法の精度より、推定値であることを失わずに運用できるかの方が、長期的な影響が大きいからです。
import numpy as npimport pandas as pd
df = pd.DataFrame( { "user_id": [1, 2, 3, 4, 5, 6], "age": [28, np.nan, 45, np.nan, 33, 39], "region": ["都心", "郊外", "地方", "郊外", "都心", "郊外"], })
med_by_region = df.groupby("region")["age"].median()group_est = df["region"].map(med_by_region)
# その群に実測が1件も無いと群別中央値は NaN になる。全体中央値で受け止めるfallback = df["age"].median()
# 埋める「前」に、どの行をどう推定したのかを記録しておくdf["age_is_estimated"] = df["age"].isna()df["age_source"] = np.select( [~df["age_is_estimated"], group_est.notna()], ["observed", "estimated:region_median"], default="estimated:overall_median",)# 元のカラムは残したまま、埋めた結果は別カラムに置くdf["age_filled"] = df["age"].fillna(group_est).fillna(fallback)
print(df[["user_id", "age", "age_filled", "age_source"]].to_string(index=False))
# 下流はいつでも「実測だけ」に戻れるobserved_only = df[~df["age_is_estimated"]]print(f"\n実測のみ: {len(observed_only)} 件 / 全体 {len(df)} 件")群別補完には見落としやすい罠があります。その群に実測が1件も存在しないと、群の代表値そのものが計算できず、結果は埋まらないまま静かに残ります。上のコードで全体中央値へのフォールバックを入れているのはそのためで、さらに「どちらで埋めたか」を出どころとして区別しています。件数の少ない群を細かく切るほどこの状況は増えるので、群の粒度は精度だけでなく充足率でも決める必要があります。
- 元カラムを潰さない: `age` は残し、埋めたものは `age_filled` に置く
- 出どころを持つ: どの手法で埋めたかを文字列で残すと、後から手法を評価できる
- 下流に「実測だけ」の経路を残す: 経営報告や請求のように推定値を使ってはいけない用途が必ずある
- 集計時に混ぜたかを明示する: 「推定を含む」と注記のないダッシュボードは、いずれ誤解を生む
7. 検証とリーケージ
補完の検証で最も多い事故が です。データ全体で計算した平均や中央値を使って埋めてから、訓練と検証に分ける。これをやると検証データの情報が補完値に混ざり、検証の点数だけが良くなります。
- 1先に分割する。補完の前に訓練・検証を分ける
- 2補完の基準は訓練データだけから計算する(平均・中央値・モデル、すべて)
- 3検証データにはその基準を適用するだけにする
- 4人工欠損での評価は、欠測の起き方を実データに似せる。ランダムに欠かせて評価すると、実際より良い数字が出る
8. やってはいけない推定
技術的にできることと、やってよいことは別です。日本の個人情報保護法には「要配慮個人情報」という区分があり、人種・信条・社会的身分・病歴・犯罪の経歴などが該当します。これらは取得や取り扱いに厳しい条件が課されます。
実務で押さえるべき点は次のとおりです。推定した結果も個人に関する情報になりうること、そして「推定できる」は「推定してよい」を意味しないことです。具体的な適法性の判断は事案ごとに異なるので、該当しそうな属性を扱う場合は必ず法務に確認してください。この記事は法的助言ではありません。
属性推定の入力になるのは、購買履歴・自由記述・問い合わせ内容といった極めて機微な情報です。外部サービスへ送る構成にする前に、手元で完結できないかを検討する価値があります。ローカル LLM の道具箱 にその選択肢をまとめています。
9. 落とし穴
- 決定的な補完を飛ばす: 対応表で確定できた値まで推定値になる。必ず ① から
- 欠測の理由を考えない: 欠けること自体が情報の場合、埋めると情報が消える
- 全体平均で埋めて分散を語る: 分布が痩せているので、その後の分散・相関は歪む
- 推定値を元カラムに上書きする: 後から品質を説明できなくなる
- : 分割前に補完すると検証の点数だけ良くなる
- 推定値で推定値を作る: 誤差が掛け算で増える。推定は1段まで
- 推定の再現性を測らない: 同じ入力で出力が揺れる前提で扱う
- 要配慮情報を無自覚に推定する: 技術判断ではなく法務判断の領域
10. ふくふくの進め方
「データが足りないので推定で補いたい」というご相談では、まず3分類のどれをやろうとしているのかを切り分けます。決定的な補完で済む部分が想像以上に残っていることが多く、そこを先に埋めるだけで推定の必要範囲が縮みます。そのうえで、検証手段が用意できる推定だけを本番に載せるという順序で設計しています。
11. ここまでのまとめ
- リッチ化は ①決定的な補完 ②統計的な補完 ③推論による付与 に分かれ、検証のしやすさが根本的に違う
- 必ず ① から着手する。対応表で確定できる分を推定値にしない
- 手法選びの前に (なぜ欠けているか)を考える。欠けること自体が情報なら埋めない
- 補完は分布を痩せさせる。精度とは別の副作用として意識する
- 推定値を元カラムに混ぜない。出どころを持ち、実測だけに戻れる経路を残す
- 分割してから補完する。 は検証の点数だけを良くする
- 推定は1段まで。推定値を入力にした推定は誤差が増幅する
- 要配慮情報の推定は法務の領域。できることとやってよいことは別
リッチ化は「データを増やす作業」に見えて、実態は 「不確かさを持ち込む作業」 です。だからこそ、どこまでが実測でどこからが推定なのかを失わない設計が要ります。サンプルから母集団を語る側の話は 指標設計の教科書 EP.19 に、欠損値そのものの基礎は EP.02 にあります。続編では、多重代入の実装、属性推定モデルの偏りの測り方、LLM 推定の一致率検証などを、読者リアクションに応じて随時追加していきます。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。