Python | データ処理:欠損値可視化

Python
スポンサーリンク

概要(欠損値可視化は「どこで、どれだけ、どう偏っているか」を一目で掴むための入口)

欠損値は“壊れたデータ”ではなく“情報が抜けている状態”です。可視化の目的は、列ごとの欠損量、行方向の偏り(特定期間・条件で集中するか)、列間の同時欠損の関係を把握し、後続の補完戦略や解析範囲を正しく設計すること。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)  # 欠損パターンで列を階層クラスタ
Python

matrixは“時間的な帯”が読みやすく、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")
Python

dtypeとインデックス整備が先

日付列は必ず to_datetime → sort(昇順)で時系列のヒートマップが正しく並びます。objectのままだと崩れます。

大規模データはサンプリング+代表ビューで

行が数百万の場合、matrix系は重くなります。期間別・グループ別に代表サンプルを抜き、列ごとの欠損率推移(折れ線)やヒートマップのサブセットを段階的に確認します。


実践ワークフロー(可視化→原因仮説→補完戦略へ)

手順の最小セット

  1. 列ごとの欠損件数・割合を棒で俯瞰(優先度決定)
  2. 行×列ヒートマップやmissingno.matrixで“帯”を把握(時間・IDの偏り)
  3. missingno.heatmapで同時欠損の関連列を確認(補完の鍵列を特定)
  4. 時系列なら曜日×時間ヒートマップ、連続欠損の抽出(運用仮説)
  5. 仮説に基づき、削除・単純補完(中央値/最頻値)・モデル補完・除外範囲を設計
# 例:最終レポート用の可視化まとめ
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を組み合わせれば、初心者でも“原因が語れる”可視化が短いコードで作れます。可視化はゴールではなく、補完戦略・除外範囲・運用改善へつなぐためのスタートラインです。

タイトルとURLをコピーしました