概要(欠損値可視化は「どこで、どれだけ、どう偏っているか」を一目で掴むための入口)
欠損値は“壊れたデータ”ではなく“情報が抜けている状態”です。可視化の目的は、列ごとの欠損量、行方向の偏り(特定期間・条件で集中するか)、列間の同時欠損の関係を把握し、後続の補完戦略や解析範囲を正しく設計すること。pandasだけでも十分描けますが、missingnoを使うと欠損のパターンを直感的に掴めます。
欠損の把握の基本(件数・割合と行×列のヒートマップ)
列ごとの欠損件数・割合をグラフで確認する
まずは「どの列がどれくらい欠損しているか」を棒グラフで俯瞰します。割合も併記すると意思決定が早くなります。
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({
"name": ["A", None, "C", "D", None],
"age": [20, 25, None, 30, 22],
"score":[80, None, 75, 90, None]
})
null_count = df.isna().sum()
null_pct = (df.isna().mean() * 100).round(1)
fig, ax = plt.subplots(figsize=(6,4))
null_count.plot(kind="bar", ax=ax, color="#e15759", alpha=0.7)
for i, v in enumerate(null_count):
ax.text(i, v, f"{v} ({null_pct.iloc[i]}%)", ha="center", va="bottom")
ax.set_title("Missing per column")
ax.set_ylabel("Count"); ax.grid(True, axis="y", alpha=0.3)
plt.tight_layout(); plt.show()
Python棒の上に「件数(割合%)」を注記すると、議論がスムーズになります。
行×列のヒートマップで“欠損の帯”を探す
行にサンプル、列に項目を並べ、欠損を1、非欠損を0にしてヒートマップ化します。期間やグループでの欠損集中が見えます。
import seaborn as sns
mask = df.isna().astype(int)
sns.heatmap(mask, cmap="Reds", cbar=False)
Python横に長い帯が見えれば「その列に継続的な欠損」、縦の帯は「特定行(期間やID)で全体的に欠損」と読めます。
missingnoの活用(matrix・bar・heatmap・dendrogram)
インストールと基本の描画
missingnoは欠損パターン可視化に特化した軽量ライブラリです。
pip install missingno
import missingno as msno
msno.matrix(df) # 行×列で欠損の有無(白=非欠損、黒=欠損)
msno.bar(df) # 列ごとの非欠損件数(棒)、欠損割合が右に併記
msno.heatmap(df) # 列間の欠損の相関(同時欠損の関連)
msno.dendrogram(df) # 欠損パターンで列を階層クラスタ
Pythonmatrixは“時間的な帯”が読みやすく、heatmapは「一緒に欠損しがちな列」を示します。dendrogramは補完順序や関連列の仮説に役立ちます。
時系列の欠損可視化(カレンダーと連続欠損の検出)
曜日×時間のカレンダーヒートマップでピーク・穴を確認
時系列ログは曜日×時間に展開して欠損の偏りを確認します。
log = pd.DataFrame({
"dt": pd.date_range("2025-01-01", periods=200, freq="H"),
"val": [1,2,5,3,4]*40
})
log.loc[::7, "val"] = None # 例として欠損を混ぜる
grid = log.assign(weekday=log["dt"].dt.day_name(),
hour=log["dt"].dt.hour) \
.pivot_table(index="weekday", columns="hour", values="val",
aggfunc=lambda s: s.isna().mean(), fill_value=0)
# 各セルは欠損割合(0〜1)
sns.heatmap(grid, cmap="magma", vmin=0, vmax=1)
Python時間帯や曜日で欠損が偏っていれば、収集バッチや祝日などの運用要因を疑えます。
連続欠損の区間を見つけて注記する
単発欠損と“連続欠損(ギャップ)”はリスクが違います。連続区間を抽出してハイライトします。
s = log.set_index("dt")["val"]
missing = s.isna().astype(int)
block_id = (missing.diff().fillna(0) == 1).cumsum() * missing # 欠損ブロックID
blocks = s[missing == 1].groupby(block_id).apply(lambda g: (g.index.min(), g.index.max()))
print(blocks.head())
Python長い連続欠損は補完ではなく“除外”や“運用改善”の検討が必要です。
前処理と可視化の順番(型・空文字・大規模対応の深掘り)
欠損の定義をそろえる(NaN/None/空文字/特殊値)
- 文字列の空白や“未知”“N/A”などを正規化して欠損扱いに寄せます。
df["name"] = df["name"].replace({"": None, "N/A": None, "未知": None})
Python- 数値は to_numeric(errors=”coerce”) で“変換不可→NaN”にして可視化へ。
df["age"] = pd.to_numeric(df["age"], errors="coerce")
Pythondtypeとインデックス整備が先
日付列は必ず to_datetime → sort(昇順)で時系列のヒートマップが正しく並びます。objectのままだと崩れます。
大規模データはサンプリング+代表ビューで
行が数百万の場合、matrix系は重くなります。期間別・グループ別に代表サンプルを抜き、列ごとの欠損率推移(折れ線)やヒートマップのサブセットを段階的に確認します。
実践ワークフロー(可視化→原因仮説→補完戦略へ)
手順の最小セット
- 列ごとの欠損件数・割合を棒で俯瞰(優先度決定)
- 行×列ヒートマップやmissingno.matrixで“帯”を把握(時間・IDの偏り)
- missingno.heatmapで同時欠損の関連列を確認(補完の鍵列を特定)
- 時系列なら曜日×時間ヒートマップ、連続欠損の抽出(運用仮説)
- 仮説に基づき、削除・単純補完(中央値/最頻値)・モデル補完・除外範囲を設計
# 例:最終レポート用の可視化まとめ
fig, axes = plt.subplots(2, 2, figsize=(10,8))
df.isna().sum().plot(kind="bar", ax=axes[0,0], color="#e15759", alpha=0.7); axes[0,0].set_title("Missing per column")
sns.heatmap(df.isna().astype(int), ax=axes[0,1], cmap="Reds", cbar=False); axes[0,1].set_title("Row×Col missing map")
msno.heatmap(df, ax=axes[1,0]); axes[1,0].set_title("Missing correlation")
msno.matrix(df, ax=axes[1,1]); axes[1,1].set_title("Missing matrix")
plt.tight_layout(); plt.show()
Pythonまとめ(「件数・割合→帯→関連→時間」を順に見て、補完設計へつなぐ)
欠損値可視化の要は、列の欠損量と割合で全体像を掴み、行×列の“帯”で偏りを見つけ、列間の同時欠損(関連)を把握し、時系列なら曜日×時間や連続欠損を確認すること。欠損の定義を揃え(空文字や特殊値をNaNへ)、dtypeと並びを整えた上で描く。missingnoとpandas/seabornを組み合わせれば、初心者でも“原因が語れる”可視化が短いコードで作れます。可視化はゴールではなく、補完戦略・除外範囲・運用改善へつなぐためのスタートラインです。
