Python | データ処理:棒+折れ線

Python
スポンサーリンク

概要(棒+折れ線は「量と推移」を一枚で伝える定番の複合グラフ)

棒グラフは“量”の比較、折れ線グラフは“推移”や“傾向”の読み取りに向いています。2つを重ねることで「月別売上の量(棒)とユーザー数の推移(線)」のように、違う性質の情報を一度に伝えられます。重要なのは、軸の扱い(同軸か二軸か)、時系列の整列、色と凡例の明確化です。


基本の重ね方(同じ軸で棒+折れ線)

最小例(同じ単位なら同じ軸で重ねる)

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", linewidth=2, label="Users")

ax.set_title("Monthly Sales and Users")
ax.set_xlabel("Month")
ax.set_ylabel("Value")
ax.legend()
ax.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

同じスケールで比較可能なら、一本のAxesに重ねるのがシンプルで見やすいです。棒は透明度を少し下げると、線が埋もれません。

時系列をインデックスにして自然な軸へ

df = pd.DataFrame({
    "date": pd.date_range("2025-01-01", periods=6, freq="D"),
    "sales": [100, 120, 90, 140, 110, 130],
    "users": [50, 55, 53, 60, 58, 62]
}).set_index("date")

ax = df[["sales"]].plot(kind="bar", color="#4e79a7", alpha=0.6, figsize=(7, 4), label="Sales")
df["users"].plot(ax=ax, color="#f28e2c", marker="o", linewidth=2, label="Users")

ax.set_title("Daily Sales and Users")
ax.set_ylabel("Value")
ax.legend()
ax.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

DateTimeIndexを使うと、目盛りや範囲指定が自然になり、時系列との相性が良くなります。


二軸グラフ(単位や桁が違うときの安全な重ね方)

左右で単位を分ける(売上と成長率など)

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]  # 比率
})

fig, ax1 = plt.subplots(figsize=(6, 4))
ax2 = ax1.twinx()

ax1.bar(df["month"], df["revenue"], color="#4e79a7", alpha=0.6, label="Revenue")
ax2.plot(df["month"], df["growth_pct"], color="#e15759", marker="o", linewidth=2, label="Growth rate")

ax1.set_ylabel("Revenue (10k JPY)")
ax2.set_ylabel("Growth (%)")
ax1.set_xlabel("Month")
ax1.set_title("Revenue and Growth")

lines = ax1.lines + ax2.lines
labels = [l.get_label() for l in lines]
ax1.legend(lines, labels)

ax1.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

単位が違う場合は二軸に分け、軸ラベルに単位を明記します。凡例は両軸の線をまとめて統合します。


整形のポイント(頻度の違い・スケール調整・見やすさの工夫)

異なる頻度を“揃えてから”重ねる

import pandas as pd

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

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

users_daily = users["users"].resample("D").ffill()
merged = pd.concat([sales["sales"], users_daily], 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

視覚的に重ねたいが桁が違う場合は、0〜1の正規化や標準化で同じ軸に載せる手もあります。解釈に注意し、凡例や注記で“正規化済み”を明示します。

見やすさの工夫(透明度・線幅・グリッド)

棒はalpha=0.5〜0.7にして折れ線を太め(linewidth=2〜3)に。yグリッドを薄く入れると量の読み取りが早くなり、凡例は短い語で重複色を避けます。目盛り・ラベルは単位を含めて誤読を防ぎます。


実践例(売上×移動平均、在庫×販売、割合+量)

日次売上(棒)と7日移動平均(線)

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(kind="bar", ax=ax, color="#4e79a7", alpha=0.6, label="Daily Sales")
ma7.plot(ax=ax, color="#f28e2c", linewidth=3, label="7D Moving Avg")

ax.set_title("Daily Sales with 7D Moving Average")
ax.set_ylabel("Sales")
ax.legend()
ax.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

棒で日々のばらつきを、線で傾向を示せます。移動平均は期間窓(”7D”)が不等間隔にも自然です。

在庫推移(線)と販売数(棒)を同軸で

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, ax = plt.subplots(figsize=(7, 4))
df["sales"].plot(kind="bar", ax=ax, color="#4e79a7", alpha=0.6, label="Sales")
df["stock"].plot(ax=ax, color="#59a14f", linewidth=2, label="Stock")

ax.set_title("Stock and Sales")
ax.set_ylabel("Value")
ax.legend()
ax.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

同じ軸上でも“量(棒)”と“推移(線)”を視覚的に分けられます。

量(棒)と割合(線)を二軸で

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "month": ["Jan","Feb","Mar","Apr","May"],
    "orders": [100, 120, 90, 140, 160],
    "return_rate": [0.05, 0.04, 0.07, 0.03, 0.06]
})

fig, ax1 = plt.subplots(figsize=(7, 4))
ax2 = ax1.twinx()

ax1.bar(df["month"], df["orders"], color="#4e79a7", alpha=0.6, label="Orders")
ax2.plot(df["month"], df["return_rate"], color="#e15759", marker="o", linewidth=2, label="Return rate")

ax1.set_ylabel("Orders (count)")
ax2.set_ylabel("Return rate (%)")
ax1.set_title("Orders and Return Rate")
lines = ax1.lines + ax2.lines
labels = [l.get_label() for l in lines]
ax1.legend(lines, labels)
ax1.grid(True, axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
Python

量と割合のようにスケールが違うものは、二軸で誤解なく伝えます。


つまずき対策(時系列の揃え・単位の明示・凡例の統合・保存)

時系列は揃える

重ねる前にto_datetimeで日時型へ、sort_valuesで昇順へ、頻度が違えばresample+補間で合わせます。タイムゾーンが混在する場合は一度UTCへ統一してからローカルへ。

単位は軸ラベルに明記

二軸では左右の単位が異なるため、軸ラベルに単位を必ず含めます。タイトルやキャプションでも補足して誤読を防ぎます。

凡例は統一

twinxの場合、左右のラインをまとめてlegendへ渡し、色の重複や曖昧なラベルを避けます。線種やマーカーで系列の識別を強めます。

仕上げと出力

tight_layoutで余白を整え、figsizeで比率を調整。レポート用はplt.savefig(“chart.png”, dpi=200)やPDF/SVGで高解像度保存します。


まとめ(「量は棒、推移は線」。単位と時間軸を整え、重ねるか二軸かを選ぶ)

棒+折れ線の複合は、量の比較と推移の読み取りを一枚で両立させる強力な手法です。単位や桁が同じなら同軸で重ね、違うなら二軸で分ける。重ねる前に時系列を揃え、色・透明度・凡例・軸ラベルを丁寧に整える。これだけで、初心者でも“誤解なく情報量が多い”グラフを安定して作れます。

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