実務データに欠損値が含まれることは珍しくありません。しかし、欠損値を「とりあえず平均値で埋める」「欠損行を削除する」といった機械的な処理は、分析結果にバイアスを持ち込むリスクがあります。重要なのは、なぜ欠損が生じたのか、その欠損パターンにはどんな構造があるのかを理解した上で、適切な対処法を選択することです。この記事では、欠損値の分類体系から高度な分析手法、そして Qast の自動欠損値分析まで包括的に解説します。
欠損メカニズムの3分類 — MCAR・MAR・MNAR
統計学では、欠損の発生メカニズムを3つのカテゴリに分類します。この分類は、欠損値の処理方法を選択する際の最も重要な判断基準です。欠損メカニズムを誤って判断すると、補完によってバイアスが増幅される可能性があるため、慎重な分析が求められます。
- 1
MCAR(Missing Completely At Random) — 完全にランダムな欠損
欠損の発生が、観測されたデータにも欠損値自体にもまったく依存しないケースです。例えば、センサーの一時的な故障によるデータ欠落がこれに当たります。MCAR の場合、欠損行を削除しても分析にバイアスは生じません。ただし、実世界で MCAR が成立するケースは多くありません。
- 2
MAR(Missing At Random) — 条件付きランダムな欠損
欠損の発生が、他の観測された変数に依存するケースです。例えば「若年層ほど年収の回答を拒否する傾向がある」場合、年収の欠損は年齢に依存していますが、年収そのものには依存していません。MAR では、条件となる変数を考慮した補完手法(多重代入法など)が有効です。
- 3
MNAR(Missing Not At Random) — 非ランダムな欠損
欠損の発生が、欠損値そのものに依存するケースです。例えば「高年収の人ほど年収を回答しない」場合がこれに該当します。MNAR は最も対処が難しく、ドメイン知識に基づいた慎重な分析が必要です。欠損そのものが情報を持つため、「欠損フラグ」を特徴量として追加することも有効な戦略です。
欠損率の統計 — カラムごとの欠損状況を把握する
まず各カラムの欠損数・欠損率を一覧で確認します。欠損率の分布を見ることで、データ全体の品質レベルを把握できます。欠損率が0%のカラム(完全データ)、1〜5%のカラム(軽微な欠損)、5〜50%のカラム(中程度の欠損)、50%以上のカラム(重度の欠損)に分けて整理すると、対処の優先順位が明確になります。
import pandas as pd
import numpy as np
df = pd.read_csv("dataset.csv")
# 欠損値の詳細統計
def missing_value_report(df: pd.DataFrame) -> pd.DataFrame:
"""カラムごとの欠損値レポートを生成する。"""
missing = df.isnull().sum()
missing_pct = (missing / len(df) * 100).round(2)
dtypes = df.dtypes
report = pd.DataFrame({
"データ型": dtypes,
"非欠損数": len(df) - missing,
"欠損数": missing,
"欠損率(%)": missing_pct,
})
return report.sort_values("欠損率(%)", ascending=False)
report = missing_value_report(df)
print(report)
# 欠損率の区分集計
bins = [0, 0.01, 5, 50, 100]
labels = ["完全データ", "軽微(1-5%)", "中程度(5-50%)", "重度(50%+)"]
report["欠損区分"] = pd.cut(report["欠損率(%)"], bins=bins, labels=labels)
print("\n欠損区分の集計:")
print(report["欠損区分"].value_counts())欠損パターンヒートマップ — 欠損の構造を可視化する
欠損パターンヒートマップは、データセット全体の欠損状況を行×列のマトリックスで可視化します。各セルを「データあり(白)」「欠損(黒)」で色分けすることで、欠損がどのカラムに集中しているか、特定の行で複数カラムが同時に欠損しているかなどのパターンが直感的に分かります。帯状のパターンが見えれば、特定の期間やソースからのデータ収集に問題があった可能性を示唆します。
import matplotlib.pyplot as plt
import seaborn as sns
# 欠損パターンヒートマップ
fig, ax = plt.subplots(figsize=(12, 8))
sns.heatmap(
df.isnull().astype(int),
cbar=False,
cmap="binary",
yticklabels=False,
ax=ax,
)
ax.set_title("欠損パターンヒートマップ")
ax.set_xlabel("カラム")
ax.set_ylabel("行")
plt.tight_layout()
plt.savefig("missing_heatmap.png", dpi=150)
plt.show()欠損の共起行列 — カラム間の欠損の関連性
共起行列(Co-occurrence Matrix)は、2つのカラムが同時に欠損している頻度を可視化します。カラムAとカラムBが高い頻度で同時に欠損している場合、それらは同じデータソースから取得されている、同じ収集プロセスで問題が起きている、あるいは因果関係があるなどの仮説を立てることができます。共起分析はMARやMNARの判別にも役立ちます。
欠損の共起パターンが見つかった場合は、データ収集プロセスを見直すことで根本原因を解決できる場合があります。Qast の欠損値分析画面では、共起行列が自動生成され、強い共起パターンがある組み合わせがハイライト表示されます。
Little の MCAR 検定 — 欠損のランダム性を統計的に検証する
Little の MCAR 検定は、データの欠損が MCAR(完全ランダム)であるかどうかを統計的に検証する手法です。帰無仮説は「欠損は MCAR である」で、p 値が有意水準(0.05)未満であれば帰無仮説が棄却され、欠損は MCAR ではない(MAR または MNAR の可能性がある)と判断されます。この検定結果は、補完手法の選択に直接影響します。
# Little の MCAR 検定(pyampute パッケージを使用)
# pip install pyampute
from pyampute.exploration.mcar_statistical_tests import MCARTest
# 数値カラムのみを対象に検定
numeric_df = df.select_dtypes(include=["number"])
mt = MCARTest(method="little")
result = mt.fit(numeric_df)
print(f"検定統計量: {result.statistic:.4f}")
print(f"自由度: {result.df}")
print(f"p値: {result.pvalue:.6f}")
if result.pvalue < 0.05:
print("→ MCARではない可能性が高い(MAR/MNARの疑い)")
else:
print("→ MCARの仮定を棄却できない")欠損パターンに基づく補完戦略の選択
欠損メカニズムの分析結果に基づいて、最適な補完戦略を選択します。MCAR であれば単純な手法(平均値補完、中央値補完)でもバイアスは生じにくいですが、MAR の場合は他の変数の情報を活用する手法が有効です。MNAR の場合は、ドメイン知識に基づいた判断が不可欠です。
- 1
MCAR の場合:リストワイズ削除 or 単純補完
欠損率が低ければリストワイズ削除(欠損行を削除)が最もシンプルで安全です。補完する場合は、数値型なら中央値、カテゴリ型なら最頻値が一般的です。
- 2
MAR の場合:多重代入法 or 予測モデル補完
他の変数の情報を使って欠損値を推定する手法が適しています。多重代入法(MICE)は、複数の補完データセットを作成して分析のばらつきを考慮できるため、学術的にも推奨されています。
- 3
MNAR の場合:ドメイン知識 + 欠損フラグ
欠損そのものが情報を持つため、「欠損しているかどうか」をバイナリ特徴量として追加することが有効です。補完する場合は、欠損メカニズムの仮定を明示した上で感度分析を実施します。
Python で実装する補完手法の比較
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.experimental import enable_iterative_imputer # noqa
from sklearn.impute import IterativeImputer
import pandas as pd
import numpy as np
# --- 1. 単純補完(平均値・中央値・最頻値) ---
mean_imputer = SimpleImputer(strategy="mean")
df_mean = pd.DataFrame(
mean_imputer.fit_transform(df[numeric_cols]),
columns=numeric_cols,
)
# --- 2. KNN 補完 ---
knn_imputer = KNNImputer(n_neighbors=5)
df_knn = pd.DataFrame(
knn_imputer.fit_transform(df[numeric_cols]),
columns=numeric_cols,
)
# --- 3. MICE(多重代入法) ---
mice_imputer = IterativeImputer(
max_iter=10, random_state=42
)
df_mice = pd.DataFrame(
mice_imputer.fit_transform(df[numeric_cols]),
columns=numeric_cols,
)
print("各手法の補完後の統計量比較:")
for name, imputed in [("元データ", df), ("平均値", df_mean),
("KNN", df_knn), ("MICE", df_mice)]:
print(f"\n{name}:")
print(imputed[numeric_cols].describe().loc[["mean", "std"]].round(3))Qast の欠損値分析機能
Qast の EDA 機能では、データセットをアップロードすると欠損値の分析が自動的に実行されます。各カラムの欠損率、欠損パターンヒートマップ、共起行列が視覚的に表示され、欠損率の閾値に基づいた推奨アクション(削除・補完・欠損フラグ追加)も提案されます。さらに、学習時には選択した前処理パイプラインに応じて最適な補完手法が自動適用されるため、ユーザーが個別に補完処理を実装する必要はありません。
欠損値の分析は単独で行うのではなく、データ監査や外れ値検出と併せて実施するのが効果的です。例えば、外れ値を先に処理してから補完を行うことで、補完結果の品質が向上します。Qast では、これらの分析が EDA パイプラインとして統合されているため、一貫した分析が可能です。
まとめ — 欠損値と正しく向き合う
欠損値の分析は、機械学習パイプラインにおいて見過ごされがちですが、モデルの精度と信頼性に直結する重要なステップです。MCAR・MAR・MNAR の分類を理解し、ヒートマップや共起行列で欠損の構造を可視化し、Little の MCAR 検定で統計的に検証する——これらのプロセスを経ることで、根拠に基づいた補完戦略を選択できるようになります。Qast を使えばこれらの分析がワンクリックで完了するため、分析時間を大幅に短縮しながら高品質なデータ前処理を実現できます。

