Python | データ処理:グラフの複合化

Python
スポンサーリンク
  1. 概要(グラフの複合化は「一枚で複数の視点」を重ねて伝える技)
  2. 基本の考え方(重ねる・二軸・分割の3パターンを使い分ける)
    1. 同じ軸で重ねる(単位が同じなら一枚に集約)
    2. 二軸グラフで重ねる(単位が違うならsecondary_y)
    3. サブプロットで分割する(時間差・密度差が大きいとき)
  3. 同じキャンバスに重ねる(折れ線+棒、複数系列オーバーレイ)
    1. 折れ線と棒グラフを重ねる最小例
    2. 同じ単位の折れ線を複数重ねる
  4. 二軸グラフ(secondary_yとtwinxの安全な使い方)
    1. pandasのsecondary_yで簡潔に二軸
    2. matplotlibのtwinxで柔軟に制御
  5. 複数サブプロット(上下や左右に分けて比較を明瞭に)
    1. sharexで時間軸を共有した上下分割
    2. 横並び比較(カテゴリ別など)
  6. 異なる頻度や欠損の整形(複合化前の“揃え”が品質を決める)
    1. 時系列を揃えてから重ねる
    2. スケールが極端に違うときは正規化して重ねる
  7. 実践例(売上×成長率、在庫×販売、日次×移動平均)
    1. 売上と成長率を二軸で一枚に
    2. 在庫推移と販売数を上下に分けて比較
    3. 日次と移動平均を同軸で重ねる(スムージング)
  8. つまずき対策(軸の単位・凡例・色/透明度・整列・保存)
    1. 軸の単位とラベルを明記する
    2. 凡例は“全系列”を統合して一箇所に
    3. 透明度(alpha)とグリッドで見やすく
    4. 時系列は“揃えてから”複合化
    5. 仕上げと保存
  9. まとめ(単位が同じなら重ねる、違うなら二軸、複雑なら分ける)

概要(グラフの複合化は「一枚で複数の視点」を重ねて伝える技)

pandasとmatplotlibを組み合わせると、折れ線と棒グラフの重ね合わせ、二重軸、複数サブプロットなどを簡潔に作れます。複合化のポイントは「軸(単位とスケール)」「凡例と色」「時系列の整列」。目的に合わせて“重ねる・分ける・二軸にする”を使い分けると、誤解なく情報量を増やせます。


基本の考え方(重ねる・二軸・分割の3パターンを使い分ける)

同じ軸で重ねる(単位が同じなら一枚に集約)

同じ単位・スケールなら、一本のAxesに複数系列を重ねるのが最も見やすいです。透明度(alpha)や線種(linestyle)で見分けます。

二軸グラフで重ねる(単位が違うならsecondary_y)

売上(万円)と成長率(%)のように単位が違うものは、右軸を追加して“スケールの独立性”を保ちます。pandasのplot(secondary_y=True)やmatplotlibのtwinxを使います。

サブプロットで分割する(時間差・密度差が大きいとき)

情報量が多い、系列数が多い、単位が多様な場合は上下/左右に分けて比較しやすくします。sharexで時間軸を共有すると読みやすくなります。


同じキャンバスに重ねる(折れ線+棒、複数系列オーバーレイ)

折れ線と棒グラフを重ねる最小例

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "month": ["Jan","Feb","Mar","Apr"],
    "sales": [100, 120, 90, 140],
    "users": [50, 55, 53, 60]
})

fig, ax = plt.subplots(figsize=(6,4))
ax.bar(df["month"], df["sales"], color="#4e79a7", alpha=0.6, label="Sales")
ax.plot(df["month"], df["users"], color="#f28e2c", marker="o", label="Users")
ax.set_ylabel("Sales")
ax.set_xlabel("Month")
ax.legend()
ax.grid(True, axis="y", alpha=0.3)
plt.show()
Python

棒は量、線は推移の“読み筋”を強調します。透明度を少し下げると重なりが見やすくなります。

同じ単位の折れ線を複数重ねる

df2 = pd.DataFrame({
    "date": pd.date_range("2025-01-01", periods=6, freq="D"),
    "A": [100, 120, 80, 130, 110, 140],
    "B": [90, 115, 85, 120, 105, 135]
}).set_index("date")

ax = df2.plot(y=["A","B"], figsize=(6,4), linewidth=2)
ax.set_title("Store A vs B")
ax.set_ylabel("Sales")
ax.grid(True, alpha=0.3)
plt.show()
Python

pandasのplotで複数列を同時に描画できます。凡例は自動生成されます。


二軸グラフ(secondary_yとtwinxの安全な使い方)

pandasのsecondary_yで簡潔に二軸

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "month": ["Jan","Feb","Mar","Apr"],
    "revenue": [100, 120, 90, 140],   # 万円
    "growth_pct": [0.10, 0.05, -0.08, 0.15]  # 比率
})

ax = df.plot(x="month", y="revenue", kind="bar", color="#4e79a7", alpha=0.6, figsize=(6,4), label="Revenue")
df.plot(x="month", y="growth_pct", ax=ax, secondary_y=True, color="#e15759", marker="o", label="Growth rate")
ax.set_ylabel("Revenue (10k JPY)")
ax.right_ax.set_ylabel("Growth (%)")
ax.grid(True, axis="y", alpha=0.3)
ax.right_ax.grid(False)
plt.show()
Python

secondary_y=Trueが右軸を追加します。左軸と右軸でラベルと単位を明確にします。

matplotlibのtwinxで柔軟に制御

import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame({
    "month": ["Jan","Feb","Mar","Apr"],
    "sales": [100, 120, 90, 140],
    "rate": [0.10, 0.05, -0.08, 0.15]
})

fig, ax1 = plt.subplots(figsize=(6,4))
ax2 = ax1.twinx()
ax1.bar(df["month"], df["sales"], color="#4e79a7", alpha=0.6, label="Sales")
ax2.plot(df["month"], df["rate"], color="#e15759", marker="o", label="Rate")

ax1.set_ylabel("Sales")
ax2.set_ylabel("Rate (%)")
lines = ax1.get_lines() + ax2.get_lines()
labels = [l.get_label() for l in lines]
ax1.legend(lines, labels)
ax1.grid(True, axis="y", alpha=0.3)
plt.show()
Python

twinxなら凡例の統合、スケール調整などを細かく制御できます。凡例は両軸のラインをまとめて渡します。


複数サブプロット(上下や左右に分けて比較を明瞭に)

sharexで時間軸を共有した上下分割

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "date": pd.date_range("2025-01-01", periods=10, freq="D"),
    "sales": [100, 120, 80, 150, 90, 110, 130, 140, 120, 160],
    "ma7":   [None, None, None, None, 108, 107, 113, 119, 121, 123]
}).set_index("date")

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(7,6))
df["sales"].plot(ax=ax1, color="#4e79a7", label="Sales")
ax1.set_title("Daily Sales")
ax1.grid(True, alpha=0.3)
ax1.legend()

df["ma7"].plot(ax=ax2, color="#f28e2c", label="7D Moving Avg")
ax2.set_title("7D Moving Average")
ax2.grid(True, alpha=0.3)
ax2.legend()

plt.tight_layout()
plt.show()
Python

同じ時間軸で異なる指標を分けると、相互関係が読みやすくなります。sharexでズームや範囲指定も連動します。

横並び比較(カテゴリ別など)

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "month": ["Jan","Feb","Mar","Apr"],
    "A": [100, 120, 90, 140],
    "B": [90, 110, 85, 130]
})

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8,4), sharey=True)
ax1.bar(df["month"], df["A"], color="#4e79a7")
ax1.set_title("Store A")
ax2.bar(df["month"], df["B"], color="#e15759")
ax2.set_title("Store B")
for ax in (ax1, ax2):
    ax.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

shareyで尺度を共通化すると、量の比較が直感的になります。


異なる頻度や欠損の整形(複合化前の“揃え”が品質を決める)

時系列を揃えてから重ねる

import pandas as pd

sales = pd.DataFrame({
    "date": pd.date_range("2025-01-01", periods=6, freq="D"),
    "val": [100, 120, 90, 140, 110, 130]
}).set_index("date")

users = pd.DataFrame({
    "date": pd.date_range("2025-01-01", periods=3, freq="2D"),
    "val": [50, 60, 55]
}).set_index("date")

users_daily = users["val"].resample("D").ffill()  # 日次へ補間
merged = pd.concat([sales["val"].rename("sales"), users_daily.rename("users")], axis=1)
Python

頻度が違う系列は、resample+ffill/bfill/interpolateで“比較可能な時間軸”へ揃えてから複合化します。

スケールが極端に違うときは正規化して重ねる

import numpy as np
merged["users_norm"] = (merged["users"] - merged["users"].min()) / (merged["users"].max() - merged["users"].min())
Python

二軸が嫌なら、正規化や標準化で同軸に重ねる選択肢もあります。解釈は要注意ですが視覚的にはシンプルです。


実践例(売上×成長率、在庫×販売、日次×移動平均)

売上と成長率を二軸で一枚に

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "month": ["Jan","Feb","Mar","Apr","May"],
    "revenue": [100, 120, 90, 140, 160],
    "growth_pct": [0.10, 0.05, -0.08, 0.15, 0.12]
})

ax = df.plot(x="month", y="revenue", kind="bar", color="#4e79a7", alpha=0.6, figsize=(7,4), label="Revenue")
df.plot(x="month", y="growth_pct", ax=ax, secondary_y=True, color="#e15759", marker="o", label="Growth")
ax.set_ylabel("Revenue (10k JPY)")
ax.right_ax.set_ylabel("Growth (%)")
ax.grid(True, axis="y", alpha=0.3)
ax.right_ax.grid(False)
plt.tight_layout()
plt.show()
Python

在庫推移と販売数を上下に分けて比較

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "date": pd.date_range("2025-01-01", periods=8, freq="D"),
    "stock": [300, 290, 280, 270, 260, 250, 240, 235],
    "sales": [20, 25, 15, 30, 18, 22, 24, 26]
}).set_index("date")

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(7,5))
df["stock"].plot(ax=ax1, color="#59a14f", linewidth=2, label="Stock")
ax1.grid(True, alpha=0.3); ax1.legend()
df["sales"].plot(ax=ax2, kind="bar", color="#4e79a7", alpha=0.7, label="Sales")
ax2.grid(True, axis="y", alpha=0.3); ax2.legend()
plt.tight_layout()
plt.show()
Python

日次と移動平均を同軸で重ねる(スムージング)

import pandas as pd
import matplotlib.pyplot as plt

s = pd.Series([100, 120, 80, 200, 150, 90, 110, 130, 140],
              index=pd.date_range("2025-01-01", periods=9, freq="D"))
ma7 = s.rolling("7D").mean()

fig, ax = plt.subplots(figsize=(7,4))
s.plot(ax=ax, color="#4e79a7", label="Daily")
ma7.plot(ax=ax, color="#f28e2c", linewidth=3, label="7D MA")
ax.legend(); ax.grid(True, alpha=0.3)
plt.show()
Python

つまずき対策(軸の単位・凡例・色/透明度・整列・保存)

軸の単位とラベルを明記する

二軸では左右の単位が必ず異なるため、軸ラベルに単位を含めます。タイトルに「売上(左)/成長率(右)」のように補足すると誤読が減ります。

凡例は“全系列”を統合して一箇所に

twinx使用時は左軸・右軸のラインをまとめてlegendへ渡して統一。色の重複を避け、線種・マーカーで区別します。

透明度(alpha)とグリッドで見やすく

棒+線の重ねでは棒をalpha=0.5〜0.7に。yグリッドを薄く入れると読み取り精度が上がります。

時系列は“揃えてから”複合化

頻度差・欠損・タイムゾーンを先に揃えます。resample+ffill/bfill/interpolate、UTC統一→ローカル変換を型にします。

仕上げと保存

tight_layoutで余白調整、figsizeで比率調整、plt.savefig(“report.png”, dpi=200)で高解像度出力。レポート用途ならSVG/PDFも検討します。


まとめ(単位が同じなら重ねる、違うなら二軸、複雑なら分ける)

グラフの複合化は「見る人が誤解せず、比較しやすい形」にすることが本質です。単位が同じなら同軸に重ね、違うならsecondary_y/twinxで二軸へ、系列が多い・密度が違うならサブプロットで分ける。頻度やタイムゾーンを揃え、凡例・色・軸ラベルを丁寧に整える。この型を守れば、初心者でも一枚の図で“伝える力が強い”可視化が着実に作れます。

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