Day 045 — データの可視化(matplotlib / seaborn)

2026-05-25 白 / Phase 1 コーディング データ可視化

📚 背景知識(読んでから問題へ)

データ分析において「見える化(可視化)」は最も強力な武器の一つです。数字だけでは気づけないパターン・分布・外れ値・相関が、グラフ1枚で一瞬で見えるようになります。Kaggleのトップランカーも、まず EDA(探索的データ分析)で大量のグラフを描くことから始めます。

💡
EDA(Exploratory Data Analysis): モデルを作る前に、データの分布・偏り・欠損・相関を視覚的に把握するプロセス。KaggleのGold以上解法のほぼ全てにEDAセクションがある。

⚖️ matplotlib vs seaborn

matplotlib

低レベルライブラリ。細かいカスタマイズが可能。全グラフの基盤になる。

import matplotlib.pyplot as plt

seaborn

matplotlibの高レベルラッパー。美しいデフォルトスタイル、統計グラフを少ないコードで描ける。

import seaborn as sns

seaborn は matplotlib の上に乗っている seaborn ↓ 内部で呼び出し matplotlib — すべての土台

どちらを使っても plt.show()plt.title()plt.tight_layout() などのmatplotlib関数は共通して使えます。

📊 主要グラフ5種

ヒストグラム

1変数の分布を見る
外れ値・偏りを発見

plt.hist() / sns.histplot()
棒グラフ

カテゴリ別の集計値
生存率の比較など

plt.bar() / sns.barplot()
散布図

2変数の関係(相関)
パターンの発見

plt.scatter() / sns.scatterplot()
箱ひげ図

分布の要約・外れ値
グループ比較に最適

sns.boxplot()
ヒートマップ

相関行列の可視化
全変数の関係を一覧

sns.heatmap()
どのグラフを選ぶか — 変数の種類で決まる 変数の数・種類は? 1変数 ヒストグラム / 箱ひげ図 sns.histplot / sns.boxplot カテゴリ×数値 棒グラフ / 箱ひげ図 sns.barplot / sns.boxplot 数値×数値 散布図 / 相関ヒートマップ sns.scatterplot / heatmap hue パラメータを使うと、カテゴリで色分けして1グラフで複数を比較できる

🖼️ 図の構造 — Figure と Axes

fig, ax = plt.subplots() — Figure と Axes の関係 fig (Figure) — キャンバス全体 ax (Axes) — グラフ1枚 ax.set_title("タイトル") ax.set_xlabel("x軸ラベル") ax.set_ylabel("y軸")

fig, ax = plt.subplots(figsize=(幅インチ, 高さインチ)) — グラフ1枚ならこの形が標準パターン。複数のグラフを並べるときは plt.subplots(行数, 列数) で作成します。

🗂️ データスキーマ(今回使用)

列名説明利用するタスク
PassengerIdint64乗客ID
Survivedint64生存フラグ (0=死亡, 1=生存)タスク2・3・4
Pclassint64チケットクラス (1=上, 2=中, 3=下)タスク2
Sexobject性別 (male/female)タスク4
Agefloat64年齢タスク1・3・4
Farefloat64運賃タスク3
Embarkedobject乗船港 (S/C/Q)

📝 問題

📌
シナリオ: 以下のTitanicサンプルデータ(50行)を使って、4種類のグラフを描いてください。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

np.random.seed(42)

data = {
    "PassengerId": range(1, 51),
    "Survived":   [0,1,1,1,0,0,0,0,1,1,0,1,1,0,0,1,0,1,1,0,
                   0,1,1,0,0,1,0,0,1,0,0,1,1,1,0,0,0,1,0,1,
                   1,0,0,1,0,1,1,0,0,1],
    "Pclass":     [3,1,3,1,3,3,1,3,3,2,3,1,3,3,2,1,3,2,3,1,
                   3,2,1,3,3,1,2,3,1,3,3,2,1,3,3,2,3,1,3,2,
                   1,3,3,2,3,1,3,2,3,1],
    "Sex":        (["male","female"]*25),
    "Age":        [22,38,26,35,28,42,54,2,27,14,40,58,20,39,14,55,
                   2,31,18,35,42,26,15,29,8,60,12,33,19,44,
                   25,35,48,22,30,41,52,17,26,31,
                   22,38,26,35,28,42,54,2,27,14],
    "Fare":       [7.25,71.28,7.92,53.10,8.05,8.46,51.86,21.07,11.13,30.07,
                   16.70,26.55,8.05,31.27,16.00,86.50,7.92,24.15,7.92,512.33,
                   7.00,7.25,9.00,7.79,7.92,71.28,12.00,15.50,26.55,8.46,
                   7.25,30.07,71.28,8.05,13.00,29.70,7.25,86.50,7.92,9.50,
                   14.00,7.25,8.05,26.55,7.92,71.28,13.00,15.50,7.92,8.05],
    "Embarked":   (["S","C","S","S","Q"]*10),
}
df = pd.DataFrame(data)

タスク1 年齢のヒストグラム(matplotlib)

Age 列のヒストグラムを matplotlib で描いてください。

  • bins=10、色: steelblue、エッジ色: white
  • タイトル: "Age Distribution"、x軸: "Age"、y軸: "Count"

タスク2 Pclassごとの生存率(seaborn barplot)

Pclass ごとの生存率(Survived の平均)を seabornbarplot で描いてください。

  • x軸: Pclass、y軸: Survived
  • タイトル: "Survival Rate by Pclass"
💡
sns.barplot は y列の平均を自動計算してくれるので、事前に集計する必要はありません。

タスク3 年齢と運賃の散布図(seaborn + hue)

AgeFare の散布図を seaborn で描き、生存フラグ(Survived)で色分けしてください。

  • hue="Survived"palette={0:"#f87171", 1:"#4ade80"}
  • タイトル: "Age vs Fare (colored by Survived)"

タスク4 性別ごとの年齢分布(箱ひげ図)

性別(Sex)ごとの Age 分布を箱ひげ図で描いてください。

  • x="Sex", y="Age", hue="Survived", palette={0:"#f87171", 1:"#4ade80"}
  • タイトル: "Age Distribution by Sex and Survived"

🔍 ヒント(段階的開示)

ヒント1 — 方向性
  • タスク1: ax.hist(df["Age"], bins=10, color="steelblue", edgecolor="white") + ax.set_title / xlabel / ylabel
  • タスク2: sns.barplot(data=df, x="Pclass", y="Survived", ax=ax) — seaborn が平均を自動計算
  • タスク3: sns.scatterplot(data=df, x="Age", y="Fare", hue="Survived", palette={0:"#f87171", 1:"#4ade80"}, ax=ax)
  • タスク4: sns.boxplot(data=df, x="Sex", y="Age", hue="Survived", palette={0:"#f87171", 1:"#4ade80"}, ax=ax)
ヒント2 — アプローチ
# タスク1
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(df["Age"], bins=10, color="steelblue", edgecolor="white")
ax.set_title("Age Distribution")
ax.set_xlabel("Age")
ax.set_ylabel("Count")
plt.tight_layout()
plt.show()

# タスク2
fig, ax = plt.subplots(figsize=(7, 5))
sns.barplot(data=df, x="Pclass", y="Survived", ax=ax)
ax.set_title("Survival Rate by Pclass")
plt.tight_layout()
plt.show()

# タスク3
fig, ax = plt.subplots(figsize=(8, 5))
sns.scatterplot(data=df, x="Age", y="Fare", hue="Survived",
                palette={0:"#f87171", 1:"#4ade80"}, ax=ax)
ax.set_title("Age vs Fare (colored by Survived)")
plt.tight_layout()
plt.show()

# タスク4
fig, ax = plt.subplots(figsize=(8, 5))
sns.boxplot(data=df, x="Sex", y="Age", hue="Survived",
            palette={0:"#f87171", 1:"#4ade80"}, ax=ax)
ax.set_title("Age Distribution by Sex and Survived")
plt.tight_layout()
plt.show()
ヒント3 — コード骨格(ほぼ答え)
# タスク1
fig, ax = plt.subplots(figsize=(8, 5))
ax.___(df["Age"], bins=___, color="___", edgecolor="___")
ax.set_title("___")
ax.set_xlabel("___")
ax.set_ylabel("___")
plt.show()

# タスク2
fig, ax = plt.subplots(figsize=(7, 5))
sns.___(data=df, x="___", y="___", ax=ax)
ax.set_title("___")
plt.show()

# タスク3
fig, ax = plt.subplots(figsize=(8, 5))
sns.___(data=df, x="Age", y="Fare", hue="Survived",
         palette={0:"___", 1:"___"}, ax=ax)
ax.set_title("___")
plt.show()

# タスク4
fig, ax = plt.subplots(figsize=(8, 5))
sns.___(data=df, x="Sex", y="Age", hue="___",
         palette={0:"___", 1:"___"}, ax=ax)
ax.set_title("___")
plt.show()

模範解答

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

np.random.seed(42)

data = {
    "PassengerId": range(1, 51),
    "Survived":   [0,1,1,1,0,0,0,0,1,1,0,1,1,0,0,1,0,1,1,0,
                   0,1,1,0,0,1,0,0,1,0,0,1,1,1,0,0,0,1,0,1,
                   1,0,0,1,0,1,1,0,0,1],
    "Pclass":     [3,1,3,1,3,3,1,3,3,2,3,1,3,3,2,1,3,2,3,1,
                   3,2,1,3,3,1,2,3,1,3,3,2,1,3,3,2,3,1,3,2,
                   1,3,3,2,3,1,3,2,3,1],
    "Sex":        (["male","female"]*25),
    "Age":        [22,38,26,35,28,42,54,2,27,14,40,58,20,39,14,55,
                   2,31,18,35,42,26,15,29,8,60,12,33,19,44,
                   25,35,48,22,30,41,52,17,26,31,
                   22,38,26,35,28,42,54,2,27,14],
    "Fare":       [7.25,71.28,7.92,53.10,8.05,8.46,51.86,21.07,11.13,30.07,
                   16.70,26.55,8.05,31.27,16.00,86.50,7.92,24.15,7.92,512.33,
                   7.00,7.25,9.00,7.79,7.92,71.28,12.00,15.50,26.55,8.46,
                   7.25,30.07,71.28,8.05,13.00,29.70,7.25,86.50,7.92,9.50,
                   14.00,7.25,8.05,26.55,7.92,71.28,13.00,15.50,7.92,8.05],
    "Embarked":   (["S","C","S","S","Q"]*10),
}
df = pd.DataFrame(data)

# ── タスク1: 年齢のヒストグラム ──
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(df["Age"], bins=10, color="steelblue", edgecolor="white")
ax.set_title("Age Distribution", fontsize=14)
ax.set_xlabel("Age")
ax.set_ylabel("Count")
plt.tight_layout()
plt.show()

# ── タスク2: Pclassごとの生存率 棒グラフ ──
fig, ax = plt.subplots(figsize=(7, 5))
sns.barplot(data=df, x="Pclass", y="Survived", ax=ax,
            palette=["#60a5fa","#4ade80","#fbbf24"])
ax.set_title("Survival Rate by Pclass", fontsize=14)
ax.set_xlabel("Pclass")
ax.set_ylabel("Survival Rate")
plt.tight_layout()
plt.show()

# ── タスク3: 年齢と運賃の散布図 ──
fig, ax = plt.subplots(figsize=(8, 5))
sns.scatterplot(data=df, x="Age", y="Fare", hue="Survived",
                palette={0:"#f87171", 1:"#4ade80"}, ax=ax, s=80)
ax.set_title("Age vs Fare (colored by Survived)", fontsize=14)
ax.set_xlabel("Age")
ax.set_ylabel("Fare")
plt.tight_layout()
plt.show()

# ── タスク4: 性別×生存の箱ひげ図 ──
fig, ax = plt.subplots(figsize=(8, 5))
sns.boxplot(data=df, x="Sex", y="Age", hue="Survived",
            palette={0:"#f87171", 1:"#4ade80"}, ax=ax)
ax.set_title("Age Distribution by Sex and Survived", fontsize=14)
ax.set_xlabel("Sex")
ax.set_ylabel("Age")
plt.tight_layout()
plt.show()

4つのタスクで使うグラフ対応表

タスクグラフ種類ライブラリ主要関数何が分かるか
タスク1ヒストグラムmatplotlibax.hist()年齢の分布・偏り
タスク2棒グラフ(平均)seabornsns.barplot()クラス別生存率の差
タスク3散布図(色分け)seabornsns.scatterplot()年齢・運賃・生存の関係
タスク4箱ひげ図(グループ比較)seabornsns.boxplot()性別×生存別の年齢分布

🪜 Step-by-Step 解説

1 fig, ax = plt.subplots() — 「台紙とグラフ」を用意する

plt.subplots() は Figure(全体のキャンバス)と Axes(グラフを描く領域)を同時に作成します。figsize=(8, 5) は「幅8インチ、高さ5インチ」の意味です。複数グラフを並べるには fig, axes = plt.subplots(2, 2, figsize=(12, 8)) と書きます。

2 ax.hist() でヒストグラムを描く

ax.hist(df["Age"], bins=10, color="steelblue", edgecolor="white")

bins=10 はデータを10個の区間に分ける指定です。edgecolor="white" を設定すると棒の境界線が白くなり、隣の棒との境界が見やすくなります。

3 sns.barplot は y列の平均を自動計算する

sns.barplot(data=df, x="Pclass", y="Survived", ax=ax)

seaborn の barplot は x のカテゴリごとに y の平均値を計算して棒グラフにします。Survived は 0/1 のフラグなので、平均値 = 生存率(0〜1)となります。95%信頼区間のエラーバーも自動で表示されます。

4 hue パラメータで色分けする

sns.scatterplot(data=df, x="Age", y="Fare", hue="Survived",
                palette={0:"#f87171", 1:"#4ade80"}, ax=ax)

hue="Survived" でグラフ内の点が Survived の値(0 or 1)によって色分けされます。palette で各値の色を辞書形式で指定できます。凡例も自動で追加されます。

5 plt.tight_layout() でラベルの重なりを防ぐ

plt.tight_layout()
plt.show()

tight_layout() は複数グラフや軸ラベルが重なるのを自動で調整します。show() の直前に書く習慣をつけましょう。Jupyter Notebook では show() がなくてもグラフが表示されますが、書いておくと明示的でわかりやすいです。

📦 箱ひげ図の解剖

箱ひげ図の各部分の意味 最大値(外れ値除く) Q3 + 1.5 × IQR 以内の最大 Q3(第3四分位)75%点 中央値(Q2)50%点 Q1(第1四分位)25%点 最小値(外れ値除く) IQR = Q3 - Q1 データの広がり 外れ値 Q1 - 1.5×IQR より小さい点 Q3 + 1.5×IQR より大きい点

箱の大きさ(IQR)が大きいほどデータのバラツキが大きく、小さいほど値が集中しています。外れ値(○)は特に注意が必要なデータ点です。

🔢 数学・統計の補足(文系向け)

ヒストグラムの「bins」とは何か

「bins(ビン)」は「箱」の意味です。データを一定の範囲で区切った箱に振り分けて、各箱に何件入るかを棒の高さで表します。Age 0〜60を bins=10 にすると、各棒は6歳刻みで「何人がその年齢帯にいるか」を示します。bins が多すぎると細かすぎて見にくく、少なすぎると分布の形が見えません。

散布図で「相関」を直感的に理解する

2変数をx軸とy軸にプロットして:

  • 点が右上がりに並ぶ → 正の相関(一方が増えると他方も増える)
  • 点が右下がりに並ぶ → 負の相関(一方が増えると他方は減る)
  • 点がバラバラ → 相関なし

グラフで見ることで、相関係数を計算する前に直感を持てます。Titanic では高い運賃(Fare)の乗客が生存しやすいことが散布図から見えます。

四分位数(箱ひげ図の基礎)

データを小さい順に並べて:

  • Q1(25%点): 下から25%の位置の値
  • Q2(50%点): 中央値(真ん中の値)
  • Q3(75%点): 下から75%の位置の値
  • IQR = Q3 - Q1: 「データの真ん中50%の広がり」。外れ値の判定に使う

🏆 Kaggleでの実践的な使い方

EDA(探索的データ分析)はKaggleコンペの必須工程です。上位解法の多くが豊富なグラフを含んでいます。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

train = pd.read_csv("train.csv")

# ① 数値列の分布を一括確認
num_cols = ["Age", "Fare", "SibSp", "Parch"]
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
for i, col in enumerate(num_cols):
    ax = axes[i//2][i%2]
    sns.histplot(train[col].dropna(), bins=30, kde=True, ax=ax)
    ax.set_title(f"{col} Distribution")
plt.tight_layout()
plt.show()

# ② カテゴリ別の生存率
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
for i, col in enumerate(["Pclass", "Sex", "Embarked"]):
    sns.barplot(data=train, x=col, y="Survived", ax=axes[i])
    axes[i].set_title(f"Survival Rate by {col}")
plt.tight_layout()
plt.show()

# ③ 相関ヒートマップ
corr = train[["Survived","Pclass","Age","SibSp","Parch","Fare"]].corr()
fig, ax = plt.subplots(figsize=(8, 6))
sns.heatmap(corr, annot=True, fmt=".2f", cmap="coolwarm", ax=ax)
ax.set_title("Correlation Heatmap")
plt.show()
テクニック何が分かるかKaggle活用場面
histplot(kde=True)分布の形(正規・偏り・外れ値)特徴量エンジニアリングの前に分布確認
barplot(y="Survived")カテゴリ別の生存率差重要特徴量の発見
heatmap(annot=True)全変数間の相関を一覧表示多重共線性・特徴量選択の参考
pairplot(hue="target")全ペアの散布図を一括生成変数間の関係を大局的に把握

Kaggle EDA の標準フロー(可視化の順番)

1
df.describe() / isna().sum() — 数値での全体把握
2
histplot(各変数の分布) — 外れ値・偏りを発見
3
barplot / boxplot(カテゴリ別比較) — 目的変数との関係
4
heatmap(相関行列) — 多重共線性・重要変数の特定
5
特徴量エンジニアリング — EDAの発見をもとに新特徴量を設計

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
plt.show() を忘れる Jupyter では自動表示されるため意識しにくい スクリプト(.py)では必須。Jupyter でも書いておくと明示的でよい
sns.barplot でy軸が 0〜1 にならず 0〜100 になる 事前に Survived をパーセント換算してしまった seaborn の barplot は y列の平均を使う。0/1 フラグそのままで渡せばよい
hue に数値型を入れて色が連続グラデーションになる hue="Survived" で int 型を渡した df["Survived"].astype(str) でカテゴリ型に変換すると 0/1 の2色に
ヒストグラムと棒グラフの混同 見た目が似ている ヒストグラム: 1変数の連続値の分布。棒グラフ: カテゴリごとの集計値
tight_layout() を省いてラベルが重なる subplot を複数使うと自動調整されない plt.tight_layout()show() の前に必ず書く

🚀 次のステップ

  • 発展: seaborn.pairplot(df, hue="Survived") で全変数ペアの散布図を一括生成。Kaggle EDA の標準ファースト・グラフ
  • 次回予告: 相関係数と散布図(df.corr() で数値化し sns.heatmap で可視化)

📋 自己評価(解いた後に記入)

自分の回答・気づき・メモ: