はじめに
データ処理ができるようになってきたので、次は可視化。
PHPではグラフ描画はフロントエンドに丸投げしていた。Chart.jsのデータをAPIで返すか、Excelに貼ってもらうかのどちらかで、サーバーサイドでグラフを生成することはほぼなかった。Pythonはサーバーサイドでグラフを作ってファイル保存できるので発想自体が違った。
ライブラリは2つ使う。matplotlibが基盤で、seabornはそれを使いやすくしたラッパー。最初はseabornだけ使えればいいかと思っていたが、細かい調整はmatplotlibを触る必要があるので両方覚えた。
インストール
pip install matplotlib seaborn
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
import pandas as pd
import numpy as np
日本語フォントの設定
デフォルトだと日本語が文字化けする。これを最初に設定しておく。
# Mac
plt.rcParams["font.family"] = "Hiragino Sans"
# Windows
plt.rcParams["font.family"] = "MS Gothic"
# 環境を選ばない方法(japanize-matplotlibを使う)
# pip install japanize-matplotlib
import japanize_matplotlib # importするだけで日本語が使えるようになる
日本語フォントの設定は最初にハマるポイントなので先に解決しておく。
matplotlib基本 — グラフの構造を理解する
matplotlibにはFigureとAxesという概念がある。
Figure(キャンバス全体)
└── Axes(グラフ1つ分)
├── x軸
├── y軸
├── タイトル
└── プロット内容
# シンプルな書き方(小さいグラフ1つのとき)
plt.plot([1, 2, 3, 4], [10, 20, 15, 30])
plt.title("シンプルな折れ線グラフ")
plt.xlabel("x軸")
plt.ylabel("y軸")
plt.savefig("graph.png", dpi=150, bbox_inches="tight")
plt.show()
plt.close()
# オブジェクト指向スタイル(推奨)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot([1, 2, 3, 4], [10, 20, 15, 30])
ax.set_title("折れ線グラフ")
ax.set_xlabel("x軸")
ax.set_ylabel("y軸")
plt.tight_layout()
plt.savefig("graph.png", dpi=150, bbox_inches="tight")
plt.show()
plt.close()
後者のオブジェクト指向スタイルが推奨されている。最初に書き方を統一しておかないと混乱する。
折れ線グラフ
時系列データの可視化でよく使う。
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
# サンプルデータ
monthly_sales = pd.DataFrame({
"month": [1, 2, 3, 4, 5, 6],
"東京": [120, 135, 148, 162, 155, 178],
"大阪": [98, 105, 112, 108, 120, 115],
"福岡": [45, 52, 48, 55, 61, 58 ],
})
fig, ax = plt.subplots(figsize=(10, 6))
for region in ["東京", "大阪", "福岡"]:
ax.plot(
monthly_sales["month"],
monthly_sales[region],
marker="o", # データ点にマーカーをつける
label=region,
linewidth=2,
)
ax.set_title("月次売上推移(地域別)")
ax.set_xlabel("月")
ax.set_ylabel("売上(万円)")
ax.legend() # 凡例
ax.grid(True, alpha=0.3) # グリッド線(薄め)
ax.set_xticks(monthly_sales["month"]) # x軸の目盛りを整数に
plt.tight_layout()
plt.savefig("line_chart.png", dpi=150, bbox_inches="tight")
plt.close()
棒グラフ
カテゴリ別の比較でよく使う。
categories = ["食品", "日用品", "電化製品", "衣類", "その他"]
values = [320, 185, 240, 155, 98]
fig, ax = plt.subplots(figsize=(9, 5))
bars = ax.bar(
categories,
values,
color=["#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B3"],
edgecolor="white",
linewidth=0.5,
)
# 棒の上に数値を表示
for bar, val in zip(bars, values):
ax.text(
bar.get_x() + bar.get_width() / 2,
bar.get_height() + 5,
f"{val}",
ha="center",
va="bottom",
fontsize=11,
)
ax.set_title("カテゴリ別売上")
ax.set_ylabel("売上(万円)")
ax.set_ylim(0, max(values) * 1.15) # y軸の上限に余白
ax.grid(axis="y", alpha=0.3)
ax.spines["top"].set_visible(False) # 上の枠線を消す
ax.spines["right"].set_visible(False) # 右の枠線を消す
plt.tight_layout()
plt.savefig("bar_chart.png", dpi=150, bbox_inches="tight")
plt.close()
散布図
2変数の相関を見るときに使う。
np.random.seed(42)
n = 100
df = pd.DataFrame({
"広告費": np.random.uniform(50, 500, n),
"売上": np.random.uniform(100, 1000, n),
"地域": np.random.choice(["東京", "大阪", "福岡"], n),
})
df["売上"] = df["広告費"] * 1.8 + np.random.normal(0, 80, n)
fig, ax = plt.subplots(figsize=(8, 6))
# 地域ごとに色分けして散布
colors = {"東京": "#4C72B0", "大阪": "#DD8452", "福岡": "#55A868"}
for region, group in df.groupby("地域"):
ax.scatter(
group["広告費"],
group["売上"],
c=colors[region],
label=region,
alpha=0.6,
s=60,
)
ax.set_title("広告費と売上の関係")
ax.set_xlabel("広告費(万円)")
ax.set_ylabel("売上(万円)")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("scatter.png", dpi=150, bbox_inches="tight")
plt.close()
seaborn — matplotlibをラップした高レベルAPI
seabornを使うとより少ないコードで見栄えのいいグラフが書ける。
import seaborn as sns
# テーマ設定(whitegrid が見やすい)
sns.set_theme(style="whitegrid", palette="muted")
seabornの棒グラフ
fig, ax = plt.subplots(figsize=(9, 5))
sns.barplot(
data=df,
x="category",
y="amount",
hue="region", # グループ分け
ax=ax,
)
ax.set_title("カテゴリ×地域別売上")
ax.set_xlabel("カテゴリ")
ax.set_ylabel("売上")
ax.legend(title="地域")
plt.tight_layout()
plt.savefig("seaborn_bar.png", dpi=150, bbox_inches="tight")
plt.close()
ヒストグラム + KDE(分布の確認)
fig, ax = plt.subplots(figsize=(8, 5))
sns.histplot(
data=df,
x="amount",
bins=30,
kde=True, # 密度曲線も一緒に描く
ax=ax,
)
ax.set_title("金額の分布")
ax.set_xlabel("金額")
ax.set_ylabel("件数")
plt.tight_layout()
plt.savefig("histogram.png", dpi=150, bbox_inches="tight")
plt.close()
ヒートマップ — 相関行列の可視化
pandasの集計結果と組み合わせて使うことが多い。
# 相関行列を計算
corr_matrix = df[["広告費", "売上", "客数", "単価"]].corr()
fig, ax = plt.subplots(figsize=(7, 6))
sns.heatmap(
corr_matrix,
annot=True, # セルに数値を表示
fmt=".2f", # 小数点2桁
cmap="coolwarm",
vmin=-1,
vmax=1,
ax=ax,
)
ax.set_title("相関行列")
plt.tight_layout()
plt.savefig("heatmap.png", dpi=150, bbox_inches="tight")
plt.close()
boxplot — 分布の比較
fig, ax = plt.subplots(figsize=(8, 5))
sns.boxplot(
data=df,
x="region",
y="amount",
palette="muted",
ax=ax,
)
ax.set_title("地域別の金額分布")
ax.set_xlabel("地域")
ax.set_ylabel("金額")
plt.tight_layout()
plt.savefig("boxplot.png", dpi=150, bbox_inches="tight")
plt.close()
複数グラフを並べる
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左のグラフ
axes[0].bar(categories, values, color="#4C72B0")
axes[0].set_title("カテゴリ別売上")
axes[0].set_ylabel("売上(万円)")
axes[0].grid(axis="y", alpha=0.3)
# 右のグラフ
axes[1].plot(months, trends, marker="o", color="#DD8452")
axes[1].set_title("月次推移")
axes[1].set_xlabel("月")
axes[1].set_ylabel("売上(万円)")
axes[1].grid(True, alpha=0.3)
plt.suptitle("売上サマリー", fontsize=14, y=1.02)
plt.tight_layout()
plt.savefig("summary.png", dpi=150, bbox_inches="tight")
plt.close()
subplots(行数, 列数)で複数のAxesを作れる。レポート用に複数グラフを1ファイルにまとめるときに使う。
グラフをファイルに保存する
# PNG(ラスタ形式)
plt.savefig("graph.png", dpi=150, bbox_inches="tight")
# SVG(ベクタ形式 — 拡大しても綺麗)
plt.savefig("graph.svg", bbox_inches="tight")
# PDF
plt.savefig("graph.pdf", bbox_inches="tight")
bbox_inches="tight"を忘れるとタイトルや軸ラベルが切れることがある。毎回つけるのが無難。
dpi=150は解像度。印刷用なら300、Web表示なら72〜150が目安。
matplotlibとseabornの使い分け
| 用途 | 推奨 |
|---|---|
| 折れ線グラフ | matplotlib |
| シンプルな棒グラフ | matplotlib |
| グループ別棒グラフ | seaborn |
| 散布図(色分けなし) | matplotlib |
| 散布図(色分けあり) | seaborn |
| ヒストグラム | seaborn |
| ヒートマップ | seaborn |
| boxplot | seaborn |
| 細かいカスタマイズ | matplotlib |
seabornはDataFrameを直接渡せてhueでグループ分けが簡単なので、グループ比較の可視化に向いている。細かい見た目の調整はmatplotlibのAPIを直接触る必要がある。
まとめ
- matplotlibが基盤、seabornはそのラッパー
- Figureがキャンバス、Axesが個々のグラフ
- オブジェクト指向スタイル(
fig, ax = plt.subplots())で統一する - 日本語は
japanize_matplotlibを入れておくと楽 -
savefig()にはbbox_inches="tight"を常につける
PHPでフロントに任せていたグラフ描画をサーバーサイドで完結できるようになった。レポート生成バッチなどで使う場面が出てきそうなので引き続き慣れていきたい。