Matplotlib を中心に、基本的な可視化、図の装飾、複数サブプロット、3D 描画、さらに Seaborn と Pandas の描画機能について、順を追って学べるように整理しています。
参考として、次の記事も参照してください。
NumPyライブラリ紹介
Pandasライブラリ紹介
データサイエンティストのためのPythonライブラリ応用(上級編)
導読
- この文は、描画環境を整える -> Matplotlib で基本のグラフを描く -> 図を装飾する -> 複数グラフや 3D 図を扱う -> Seaborn と Pandas の描画へ広げる、という流れで Matplotlib 周辺の可視化の全体像をつかむためのメモです。
- 読み始めるときは、まず
plt.plot()、plt.scatter()、plt.bar()、plt.hist()、plt.subplot()、plt.legend()、plt.text()、plt.annotate()、plt.savefig()を押さえると、基本的な可視化はかなり書けるようになります。 - Matplotlib は細かな制御に強く、Seaborn は見た目の整った統計可視化に強く、Pandas の
.plot()は DataFrame を手早く可視化するのに向いています。この 3 つの役割の違いを意識して読むと理解しやすくなります。
この文の章立ては、次のようになっています。
- 描画環境を整える
-
plt.show()が必要な場面: Notebook / IPython とスクリプト実行時の違いを整理します。 - スタイルを設定する:
plt.style.use()とplt.style.context()を確認します。 - 画像を保存する:
plt.savefig()で図をファイルへ出力します。
-
- Matplotlib の基本的なグラフ
- 折れ線グラフ: 線の色、線種、太さ、マーカー、軸、ラベル、凡例、注釈をまとめて扱います。
- 散布図: 色、サイズ、透明度を使い分け、ランダムウォークの例まで確認します。
- 柱状グラフ: 単純な棒グラフ、積み上げ、並列、横棒グラフを扱います。
- 複数サブプロット:
subplot()とGridSpecによる配置を見ます。 - ヒストグラム: 頻度、確率密度、累積分布、正規分布との重ね描き、サイコロのシミュレーションを扱います。
- 誤差図:
errorbar()と誤差付き棒グラフを確認します。 - オブジェクト指向スタイル:
FigureとAxesを明示的に扱う書き方を紹介します。 - 3D 描画: 3D の線、散布図、曲面図を扱います。
- Seaborn と Matplotlib
- スタイルの違い: 同じ線グラフを Matplotlib と Seaborn で比べます。
- 棒グラフの違い:
plt.barh()とsns.barplot()を比べます。 - iris データの可視化:
pairplot()による多変量可視化を確認します。
- Pandas の描画機能
- 線グラフ:
DataFrame.plot()で時系列風の推移を描きます。 - 棒グラフ: 通常、積み上げ、横向きの棒グラフを描きます。
- ヒストグラムと KDE: 分布の概観と差分系列のヒストグラムを見ます。
- 散布図: 地理情報や住宅価格の可視化を行います。
- 複数サブプロット:
subplots=Trueやlayout=で配置を制御します。
- 線グラフ:
Matplotlib を使う理由
データ分析では、数値を表として見るだけでは気づきにくいパターンが多くあります。分布の形、外れ値、トレンド、カテゴリ差、時系列の変化は、図にした方が圧倒的に把握しやすくなります。
Matplotlib は Python における可視化の基盤となるライブラリです。基本的な折れ線グラフから、複数サブプロット、3D 描画、図の細かな装飾まで広く対応できるため、他の可視化ライブラリの土台としても使われています。
import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [1, 4, 9, 16]
# まずは最も基本的な折れ線グラフを描く
plt.plot(x, y)
plt.ylabel("2乗値")
plt.show()
描画環境を整える
最初に、Matplotlib をどの環境で使うかによって、表示方法が少し異なる点を整理します。
plt.show() が必要な場面
Notebook や IPython では %matplotlib inline を使うと、セルの出力として図がそのまま表示されます。一方で、通常の Python スクリプトや一部の IDE では plt.show() を明示しないと図が表示されないことがあります。
%matplotlib inline
import matplotlib.pyplot as plt
# Notebook で見やすいスタイルを設定する
plt.style.use("seaborn-whitegrid")
x = [1, 2, 3, 4]
y = [1, 4, 9, 16]
plt.plot(x, y)
plt.ylabel("2乗値")
# スクリプト実行時は plt.show() が必要になることがある
スタイルを設定する
plt.style.use() を使うと、グリッドや背景を含めた描画スタイルをまとめて切り替えられます。plt.style.context() を使えば、一時的にだけスタイルを変更できます。
import matplotlib.pyplot as plt
# 利用可能なスタイル名を確認する
print(plt.style.available[:5])
# context を使うとこのブロック内だけスタイルを切り替えられる
with plt.style.context("seaborn-white"):
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
画像を保存する
作成した図は plt.savefig() でファイルへ保存できます。レポート用の図や共有用の画像を残したいときに使います。
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 100)
# 指数関数の形を描いて画像として保存する
plt.plot(x, np.exp(x))
plt.savefig("my_figure.png")
Matplotlib の基本的なグラフ
この章では、Matplotlib でよく使うグラフを順に確認します。まずは plot、scatter、bar、hist が使えるようになると、かなり多くの可視化に対応できます。
折れ線グラフ
折れ線グラフは、時系列、関数の形、連続量の変化を見るときに最も基本になる図です。
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
plt.style.use("seaborn-whitegrid")
# 0 から 2π までを 100 分割する
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x))
複数の曲線を同じ座標に描くことも簡単です。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
# cos と sin を同じ座標へ重ねて描く
plt.plot(x, np.cos(x))
plt.plot(x, np.sin(x))
線の色、線種、太さ、マーカー
折れ線グラフでは、色、線種、線幅、マーカーを調整することで読みやすさが大きく変わります。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
offsets = np.linspace(0, np.pi, 5)
colors = ["blue", "g", "r", "yellow", "pink"]
# 色だけ変えて複数本の線を比較する
for offset, color in zip(offsets, colors):
plt.plot(x, np.sin(x - offset), color=color)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 11)
offsets = list(range(8))
linestyles = ["solid", "dashed", "dashdot", "dotted", "-", "--", "-.", ":"]
# 線種の違いをまとめて確認する
for offset, linestyle in zip(offsets, linestyles):
plt.plot(x, x + offset, linestyle=linestyle)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 11)
offsets = list(range(0, 12, 3))
linewidths = (i * 2 for i in range(1, 5))
# 線の太さを変える
for offset, linewidth in zip(offsets, linewidths):
plt.plot(x, x + offset, linewidth=linewidth)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 11)
offsets = list(range(0, 12, 3))
markers = ["*", "+", "o", "s"]
# マーカーを変えると点の位置が見やすくなる
for offset, marker in zip(offsets, markers):
plt.plot(x, x + offset, marker=marker, markersize=10)
色、マーカー、線種は短縮記法でも書けます。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 11)
offsets = list(range(0, 8, 2))
color_linestyles = ["g-", "b--", "k-.", "r:"]
# 色と線種を短縮記法で指定する
for offset, style in zip(offsets, color_linestyles):
plt.plot(x, x + offset, style)
x = np.linspace(0, 10, 11)
offsets = list(range(0, 8, 2))
color_marker_linestyles = ["g*-", "b+--", "ko-.", "rs:"]
# 色、マーカー、線種を 1 つの文字列でまとめる
for offset, style in zip(offsets, color_marker_linestyles):
plt.plot(x, x + offset, style)
参考: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot
軸を調整する
xlim()、ylim()、axis()、xscale()、xticks()、yticks()、tick_params() を使うと、表示範囲や目盛りを細かく制御できます。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x))
# x 軸と y 軸の表示範囲を指定する
plt.xlim(-1, 7)
plt.ylim(-1.5, 1.5)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x))
# axis() で範囲をまとめて指定する
plt.axis([-2, 8, -2, 2])
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x))
# tight は余白を詰める
plt.axis("tight")
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x))
# equal は縦横比をそろえる
plt.axis("equal")
import matplotlib.pyplot as plt
import numpy as np
x = np.logspace(0, 5, 100)
plt.plot(x, np.log(x))
# 対数目盛りへ切り替える
plt.xscale("log")
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
plt.plot(x, x**2)
# 目盛りの位置と文字サイズをそろえる
plt.xticks(np.arange(0, 12, step=1), fontsize=15)
plt.yticks(np.arange(0, 110, step=10))
plt.tick_params(axis="both", labelsize=15)
タイトル、ラベル、凡例、注釈
グラフの読みやすさは、ラベルや凡例、注釈によって大きく変わります。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x))
# タイトルと軸ラベルを付ける
plt.title("正弦曲線", fontsize=20)
plt.xlabel("x", fontsize=15)
plt.ylabel("sin(x)", fontsize=15)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
# label を付けておくと legend() で凡例を出せる
plt.plot(x, np.sin(x), "b-", label="正弦")
plt.plot(x, np.cos(x), "r--", label="余弦")
plt.legend()
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x), "b-", label="正弦")
plt.plot(x, np.cos(x), "r--", label="余弦")
plt.ylim(-1.5, 2)
# loc は凡例の位置、frameon は枠の有無、fontsize は文字サイズ
plt.legend(loc="upper center", frameon=True, fontsize=15)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x), "b-")
# text() で任意位置へ文字を置く
plt.text(3.5, 0.5, "y = sin(x)", fontsize=15)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x), "b-")
# annotate() は矢印付きの注釈を書くときに便利
# xy は注目点、xytext は説明文を置く位置
plt.annotate(
"極小値",
xy=(1.5 * np.pi, -1),
xytext=(4.5, 0),
# arrowprops で矢印の色や縮み具合を指定する
arrowprops=dict(facecolor="black", shrink=0.1),
)
散布図
散布図は、2 変数の関係や点の密度、クラスタ、外れ値を確認するのに向いています。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 20)
# marker は点の形、s はサイズ、c は色を表す
plt.scatter(x, np.sin(x), marker="o", s=30, c="r")
色に数値を割り当てると、色相でも情報を持たせられます。colorbar() を付けると読みやすくなります。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = x**2
# c に数値列を渡すと値に応じて色が変わる
# cmap はその値をどの配色で見せるかを指定する
plt.scatter(x, y, c=y, cmap="inferno")
plt.colorbar()
参考: https://matplotlib.org/examples/color/colormaps_reference.html
点ごとにサイズを変えることもできます。
import matplotlib.pyplot as plt
import numpy as np
x, y, colors, size = (np.random.rand(100) for _ in range(4))
# c は色分け用の値、s は面積ベースのサイズ指定
# cmap は色分けの対応表として使う
plt.scatter(x, y, c=colors, s=1000 * size, cmap="viridis")
透明度 alpha を下げると、点が密集している領域も見やすくなります。
import matplotlib.pyplot as plt
import numpy as np
x, y, colors, size = (np.random.rand(100) for _ in range(4))
# alpha は透明度で、0 に近いほど薄く表示される
plt.scatter(x, y, c=colors, s=1000 * size, cmap="viridis", alpha=0.3)
plt.colorbar()
例: ランダムウォーク
Notebook では、散布図の応用例としてランダムウォークを描いています。点の色を進行順に変えると、動きの流れが見やすくなります。
from random import choice
import matplotlib.pyplot as plt
class RandomWalk:
"""ランダムウォークを生成するクラス"""
def __init__(self, num_points=5000):
self.num_points = num_points
self.x_values = [0]
self.y_values = [0]
def fill_walk(self):
while len(self.x_values) < self.num_points:
# x 方向と y 方向の進み方をランダムに決める
x_direction = choice([1, -1])
x_distance = choice([0, 1, 2, 3, 4])
x_step = x_direction * x_distance
y_direction = choice([1, -1])
y_distance = choice([0, 1, 2, 3, 4])
y_step = y_direction * y_distance
if x_step == 0 or y_step == 0:
continue
# 直前の位置に今回の移動量を足す
next_x = self.x_values[-1] + x_step
next_y = self.y_values[-1] + y_step
self.x_values.append(next_x)
self.y_values.append(next_y)
rw = RandomWalk(10000)
rw.fill_walk()
# 進行順を色で表す
point_numbers = list(range(rw.num_points))
plt.figure(figsize=(12, 6))
plt.scatter(rw.x_values, rw.y_values, c=point_numbers, cmap="inferno", s=1)
plt.colorbar()
# 始点と終点を強調する
plt.scatter(0, 0, c="green", s=100)
plt.scatter(rw.x_values[-1], rw.y_values[-1], c="red", s=100)
plt.xticks([])
plt.yticks([])
柱状グラフ
棒グラフはカテゴリごとの値を比較するときに便利です。単純な棒グラフだけでなく、積み上げや並列、横向きの表示もよく使います。
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(1, 6)
# align は棒の基準位置、width は棒幅、alpha は透明度
# color は塗り色、edgecolor は枠線の色を表す
plt.bar(x, 2 * x, align="center", width=0.5, alpha=0.5, color="yellow", edgecolor="red")
plt.tick_params(axis="both", labelsize=13)
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(1, 6)
plt.bar(x, 2 * x, align="center", width=0.5, alpha=0.5, color="yellow", edgecolor="red")
# x 軸ラベルをカテゴリ名へ置き換える
plt.xticks(x, ("G1", "G2", "G3", "G4", "G5"))
plt.tick_params(axis="both", labelsize=13)
import matplotlib.pyplot as plt
import numpy as np
x = ["G" + str(i) for i in range(5)]
y = 1 / (1 + np.exp(-np.arange(5)))
colors = ["red", "yellow", "blue", "green", "gray"]
# 棒ごとに色を変える
plt.bar(x, y, align="center", width=0.5, alpha=0.5, color=colors)
plt.tick_params(axis="both", labelsize=13)
積み上げ棒グラフでは、bottom= で下に積む系列を指定します。
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(5)
y1 = np.random.randint(20, 30, size=5)
y2 = np.random.randint(20, 30, size=5)
# width は棒幅、bottom は下段系列の高さ、label は凡例名
plt.bar(x, y1, width=0.5, label="男性")
plt.bar(x, y2, width=0.5, bottom=y1, label="女性")
plt.legend()
並列に並べるときは、x 座標を少しずらします。
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(15)
y1 = x + 1
y2 = y1 + np.random.random(15)
# x 座標を少しずらして並列に描く
plt.bar(x, y1, width=0.3, label="男性")
plt.bar(x + 0.3, y2, width=0.3, label="女性")
plt.legend()
横向きの棒グラフは barh() を使います。
import matplotlib.pyplot as plt
import numpy as np
x = ["G1", "G2", "G3", "G4", "G5"]
y = 2 * np.arange(1, 6)
# height は横棒の太さ、align は棒の位置合わせに使う
plt.barh(x, y, align="center", height=0.5, alpha=0.8, color="blue", edgecolor="red")
plt.tick_params(axis="both", labelsize=13)
複数サブプロット
1 枚の図の中に複数の Axes を配置すると、比較がしやすくなります。subplot() は最も基本的な書き方です。
import matplotlib.pyplot as plt
import numpy as np
def f(t):
return np.exp(-t) * np.cos(2 * np.pi * t)
t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)
# 2 行 1 列のうち上段を選ぶ
plt.subplot(211)
plt.plot(t1, f(t1), "bo-", markerfacecolor="r", markersize=5)
plt.title("2つのサブプロット")
plt.ylabel("減衰振動")
# 下段へ別のグラフを描く
plt.subplot(212)
plt.plot(t2, np.cos(2 * np.pi * t2), "r--")
plt.xlabel("時間 (秒)")
plt.ylabel("非減衰")
多行多列の配置では、subplots_adjust() で隙間を調整できます。
import matplotlib.pyplot as plt
import numpy as np
x = np.random.random(10)
y = np.random.random(10)
# サブプロット間の余白を調整する
plt.subplots_adjust(hspace=0.5, wspace=0.3)
plt.subplot(321)
plt.scatter(x, y, s=80, c="b", marker=">")
plt.subplot(322)
plt.scatter(x, y, s=80, c="g", marker="*")
plt.subplot(323)
plt.scatter(x, y, s=80, c="r", marker="s")
plt.subplot(324)
plt.scatter(x, y, s=80, c="c", marker="p")
plt.subplot(325)
plt.scatter(x, y, s=80, c="m", marker="+")
plt.subplot(326)
plt.scatter(x, y, s=80, c="y", marker="H")
不規則な配置では GridSpec が便利です。
import matplotlib.pyplot as plt
import numpy as np
def f(x):
return np.exp(-x) * np.cos(2 * np.pi * x)
x = np.arange(0.0, 3.0, 0.01)
# 2 x 3 のグリッドを作る
grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)
plt.subplot(grid[0, 0])
plt.plot(x, f(x))
plt.subplot(grid[0, 1:])
plt.plot(x, f(x), "r--", lw=2)
plt.subplot(grid[1, :])
plt.plot(x, f(x), "g-.", lw=3)
ヒストグラム
ヒストグラムは分布の形を確認する基本ツールです。頻度、確率密度、累積分布など、描き方を変えると見えるものも変わります。
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 100, 15
# 平均 100、標準偏差 15 の乱数を作る
x = mu + sigma * np.random.randn(10000)
# bins は階級数、facecolor は棒の塗り色、alpha は透明度
plt.hist(x, bins=50, facecolor="g", alpha=0.75)
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
# bins=50 は 50 区間へ分ける指定
# density=True にすると縦軸が件数ではなく確率密度になる
plt.hist(x, 50, density=True, color="r")
plt.xlabel("IQ")
plt.ylabel("確率")
plt.title("IQ のヒストグラム")
plt.text(60, 0.025, r"$\mu=100,\ \sigma=15$")
plt.xlim(40, 160)
plt.ylim(0, 0.03)
境界線だけのヒストグラムにすると、他の曲線との重ね描きがしやすくなります。
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
# histtype="step" は塗りつぶさず輪郭線だけで描く指定
plt.hist(x, bins=50, density=True, color="r", histtype="step")
plt.xlabel("IQ")
plt.ylabel("確率")
plt.title("IQ のヒストグラム")
plt.text(60, 0.025, r"$\mu=100,\ \sigma=15$")
plt.xlim(40, 160)
plt.ylim(0, 0.03)
正規分布の理論曲線を重ねることもできます。
from scipy.stats import norm
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
# ヒストグラムと理論曲線を重ねる
_, bins, _ = plt.hist(x, 50, density=True)
y = norm.pdf(bins, mu, sigma)
plt.plot(bins, y, "r--", lw=3)
plt.xlabel("IQ")
plt.ylabel("確率")
plt.title("IQ のヒストグラム")
plt.text(60, 0.025, r"$\mu=100,\ \sigma=15$")
plt.xlim(40, 160)
plt.ylim(0, 0.03)
累積分布を見るときは cumulative=True を使います。
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
# cumulative=True で左から累積した比率を描ける、累積分布関数の形になる
plt.hist(x, 50, density=True, cumulative=True, color="r")
plt.xlabel("IQ")
plt.ylabel("累積確率")
plt.title("IQ のヒストグラム")
plt.text(60, 0.8, r"$\mu=100,\ \sigma=15$")
plt.xlim(50, 165)
plt.ylim(0, 1.1)
例: サイコロ投げのシミュレーション
ヒストグラムは離散的な確率分布の確認にも向いています。Notebook では、1 個と 2 個のサイコロの和を比較しています。
import matplotlib.pyplot as plt
import numpy as np
class Die:
"""サイコロを表すクラス"""
def __init__(self, num_sides=6):
self.num_sides = num_sides
def roll(self):
# 1 から面数までの整数を 1 つ返す
return np.random.randint(1, self.num_sides + 1)
import matplotlib.pyplot as plt
die = Die()
results = []
# 1 個のサイコロをたくさん振る
for _ in range(60000):
results.append(die.roll())
# range は棒の左右端、align は棒を目盛りの中央へ合わせる指定
plt.hist(results, bins=6, range=(0.75, 6.75), align="mid", width=0.5)
plt.xlim(0, 7)
import matplotlib.pyplot as plt
import numpy as np
die1 = Die()
die2 = Die()
results = []
# 2 個のサイコロの和を集計する
for _ in range(60000):
results.append(die1.roll() + die2.roll())
# 2 個の和は 2 から 12 までなので、11 本の棒を用意する
plt.hist(results, bins=11, range=(1.75, 12.75), align="mid", width=0.5)
plt.xlim(1, 13)
plt.xticks(np.arange(1, 14))
誤差図
観測値や推定値に誤差幅を添えたいときは、errorbar() や誤差付き棒グラフが便利です。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 50)
dy = 0.5
y = np.sin(x) + dy * np.random.randn(50)
# yerr は縦方向の誤差幅、fmt は点と線の見た目をまとめた指定
plt.errorbar(x, y, yerr=dy, fmt="+b")
import matplotlib.pyplot as plt
import numpy as np
menMeans = (20, 35, 30, 35, 27)
womenMeans = (25, 32, 34, 20, 25)
menStd = (2, 3, 4, 1, 2)
womenStd = (3, 5, 2, 3, 3)
ind = ["G1", "G2", "G3", "G4", "G5"]
width = 0.35
# width は棒幅、yerr は誤差幅、bottom は積み上げ開始位置
plt.bar(ind, menMeans, width=width, label="男性", yerr=menStd)
plt.bar(ind, womenMeans, width=width, bottom=menMeans, label="女性", yerr=womenStd)
plt.ylabel("得点")
plt.title("グループ別の得点")
plt.yticks(np.arange(0, 81, 10))
plt.legend()
オブジェクト指向スタイル
Matplotlib には plt.plot() のような状態ベースの書き方だけでなく、Figure と Axes を明示的に扱う書き方もあります。複雑な図ではこちらの方が管理しやすくなります。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 10)
y = x**2
# Figure を作って、その上へ Axes を手動配置する
fig = plt.figure(figsize=(8, 4), dpi=80)
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])
axes.plot(x, y, "r")
axes.set_xlabel("x")
axes.set_ylabel("y")
axes.set_title("タイトル")
インセット図のように、ひとつの Figure に複数の Axes を自由配置することもできます。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 10)
y = x**2
# 大きい Axes と小さい Axes を 1 つの Figure に並べる
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax2 = fig.add_axes([0.2, 0.5, 0.4, 0.3])
ax1.plot(x, y, "r")
ax1.set_xlabel("x")
ax1.set_ylabel("y")
ax1.set_title("メイン図")
ax2.plot(y, x, "g")
ax2.set_xlabel("y")
ax2.set_ylabel("x")
ax2.set_title("挿入図")
import matplotlib.pyplot as plt
import numpy as np
def f(t):
return np.exp(-t) * np.cos(2 * np.pi * t)
t1 = np.arange(0.0, 3.0, 0.01)
# subplot() を使って Axes を順番に配置する
fig = plt.figure()
fig.subplots_adjust(hspace=0.4, wspace=0.4)
ax1 = plt.subplot(2, 2, 1)
ax1.plot(t1, f(t1))
ax1.set_title("左上")
ax2 = plt.subplot(2, 2, 2)
ax2.plot(t1, f(t1))
ax2.set_title("右上")
ax3 = plt.subplot(2, 1, 2)
ax3.plot(t1, f(t1))
ax3.set_title("下段")
3D 描画
Matplotlib では mpl_toolkits.mplot3d を使うと、3D の線、散布図、曲面図を描けます。
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
import numpy as np
# 3D の座標軸を作る
ax = plt.axes(projection="3d")
zline = np.linspace(0, 15, 1000)
xline = np.sin(zline)
yline = np.cos(zline)
# 3D の線を描く
ax.plot3D(xline, yline, zline)
zdata = 15 * np.random.random(100)
xdata = np.sin(zdata)
ydata = np.cos(zdata)
# 3D の散布図も同じ Axes に重ねられる
ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap="spring")
import matplotlib.pyplot as plt
import numpy as np
def f(x, y):
return np.sin(np.sqrt(x**2 + y**2))
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
# 格子点を作って曲面の高さを計算する
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
ax = plt.axes(projection="3d")
# 曲面図を描く
ax.plot_surface(X, Y, Z, cmap="viridis")
Notebook には、曲面図として少し特殊な入力例も含まれています。こちらは 3D 配列の扱い方を試すためのサンプルと考えると分かりやすいです。
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
t = np.linspace(0, 2 * np.pi, 1000)
X = np.sin(t)
Y = np.cos(t)
Z = np.arange(t.size)[:, np.newaxis]
ax = plt.axes(projection="3d")
# 特殊な形状のデータを surface へ渡した例
ax.plot_surface(X, Y, Z, cmap="viridis")
Seaborn と Matplotlib
Seaborn は Matplotlib を土台にした統計可視化ライブラリで、Pandas の DataFrame とも相性がよいのが特徴です。見た目が整いやすく、少ないコードで見栄えのよい図を作りやすいです。
同じ線グラフを比べる
Matplotlib のクラシックスタイルと Seaborn のデフォルトスタイルを比べると、背景や配色の違いが分かりやすいです。
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 500)
y = np.cumsum(np.random.randn(500, 6), axis=0)
# classic スタイルで同じデータを描く
with plt.style.context("classic"):
plt.plot(x, y)
plt.legend("ABCDEF", ncol=2, loc="upper left")
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
x = np.linspace(0, 10, 500)
y = np.cumsum(np.random.randn(500, 6), axis=0)
# Seaborn のデフォルトスタイルへ切り替える
sns.set()
plt.figure(figsize=(10, 6))
plt.plot(x, y)
# ncol は凡例を何列で並べるか、loc は配置位置を表す
plt.legend("ABCDEF", ncol=2, loc="upper left")
棒グラフを比べる
同じ棒グラフでも、Seaborn を使うと少ないコードで整った見た目にしやすくなります。
import matplotlib.pyplot as plt
import numpy as np
x = ["G1", "G2", "G3", "G4", "G5"]
y = 2 * np.arange(1, 6)
plt.figure(figsize=(8, 4))
# Matplotlib で横向き棒グラフを描く
plt.barh(x, y, align="center", height=0.5, alpha=0.8, color="blue")
plt.tick_params(axis="both", labelsize=13)
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
plt.figure(figsize=(8, 4))
x = ["G5", "G4", "G3", "G2", "G1"]
y = 2 * np.arange(5, 0, -1)
# x と y に列を割り当て、linewidth で棒の境界線を太くする
sns.barplot(x=y, y=x, linewidth=5)
iris データを pairplot で見る
Seaborn には、複数列をまとめて見られる pairplot() のような高水準関数があります。分類問題で特徴量の分離をざっと見るときに便利です。
import seaborn as sns
iris = sns.load_dataset("iris")
# データを確認してから pairplot を描く
print(iris.head())
# hue は種別ごとに色分けする指定
sns.pairplot(data=iris, hue="species")
Pandas の描画機能
Pandas の .plot() は、Matplotlib を裏側で使いながら、DataFrame から直接グラフを書けるのが利点です。集計結果をすぐ見たいときに特に便利です。
線グラフ
時系列風のデータや累積和の推移は、DataFrame.plot() でそのまま描けます。
import numpy as np
import pandas as pd
df = pd.DataFrame(
np.random.randn(1000, 4).cumsum(axis=0),
columns=list("ABCD"),
index=np.arange(1000),
)
print(df.head())
# 列ごとに 1 本の折れ線として描画される
df.plot()
棒グラフ
Pandas では、.plot.bar() や .plot.barh() を使うと DataFrame から直接棒グラフを描けます。
import numpy as np
import pandas as pd
df2 = pd.DataFrame(np.random.rand(10, 4), columns=["a", "b", "c", "d"])
print(df2)
# 通常の棒グラフ、積み上げ棒グラフ、横向き棒グラフ
# stacked=True にすると列を積み上げ表示できる
df2.plot.bar()
df2.plot.bar(stacked=True)
df2.plot.barh(stacked=True)
ヒストグラムと KDE
分布を見るときは .plot.hist() や kind="kde" が便利です。
import numpy as np
import pandas as pd
df4 = pd.DataFrame({
"A": np.random.randn(1000) - 3,
"B": np.random.randn(1000),
"C": np.random.randn(1000) + 3,
})
print(df4.head())
# ヒストグラム、累積ヒストグラム、KDE を順に確認する
# bins は階級数、cumulative=True は累積表示
# kind="kde" は滑らかな密度曲線を描く指定
df4.plot.hist(bins=50)
df4["A"].plot.hist(cumulative=True)
df4["A"].plot(kind="kde")
差分系列のヒストグラムを見ると、変化量の分布を確認できます。
import numpy as np
import pandas as pd
df = pd.DataFrame(
np.random.randn(1000, 4).cumsum(axis=0),
columns=list("ABCD"),
index=np.arange(1000),
)
print(df.head())
# 差分を取って変化量の分布を見る
# diff() で前時点との差を作り、その分布を確認する
df.diff().hist(bins=50, color="r")
散布図
Pandas の kind="scatter" は、DataFrame の列名をそのまま指定できるので便利です。Notebook では住宅データを使って地理情報と価格の関係を描いています。
import pandas as pd
# 住宅データを読み込む
housing = pd.read_csv("housing.csv")
print(housing.head())
import matplotlib.pyplot as plt
import seaborn as sns
with sns.axes_style("white"):
# 緯度経度、人口、住宅価格を同時に表現する
housing.plot(
kind="scatter",
x="longitude",
y="latitude",
# alpha は透明度、s は点サイズ、label は凡例名
alpha=0.6,
s=housing["population"] / 100,
label="人口",
# c は色付けに使う列、cmap は配色、colorbar=True で凡例色を付ける
c="median_house_value",
cmap="jet",
colorbar=True,
figsize=(12, 8),
)
plt.legend()
# axis([xmin, xmax, ymin, ymax]) の順で表示範囲を固定する
plt.axis([-125, -113.5, 32, 43])
# 所得と住宅価格の関係を見る
# kind="scatter" で散布図、alpha で重なりを見やすくする
housing.plot(kind="scatter", x="median_income", y="median_house_value", alpha=0.8)
複数サブプロット
列ごとに独立した図として描きたいときは、subplots=True が便利です。layout= で並び方も制御できます。
import numpy as np
import pandas as pd
df = pd.DataFrame(
np.random.randn(1000, 4).cumsum(axis=0),
columns=list("ABCD"),
index=np.arange(1000),
)
print(df.head())
# subplots=True で列ごとに別グラフへ分ける
df.plot(subplots=True, figsize=(6, 16))
# layout は配置、sharex=False は x 軸を別々に持たせる指定
df.plot(subplots=True, layout=(2, 2), figsize=(16, 6), sharex=False)
参考: https://www.pypandas.cn/docs/user_guide/visualization.html#plot-formatting
まとめ
Matplotlib を最初に学ぶときは、関数を単発で覚えるよりも、次の流れでつなげて覚えると整理しやすくなります。
-
plot()、scatter()、bar()、hist()で基本的な図を描く。 -
xlim()、legend()、text()、annotate()で図を読みやすく整える。 -
subplot()やGridSpecで複数の図を並べる。 - 誤差図や 3D 図など、用途に応じた表現を追加する。
- Seaborn や Pandas の描画機能も併用し、目的に応じて使い分ける。
この流れで練習すると、単に図を描くだけでなく、分析結果をどう見せるかまで含めて考えられるようになります。