HOWTO · Matplotlib

Matplotlib の散布図でマーカーサイズを設定する

Matplotlib の散布図で全点を同じマーカーサイズにする方法、点ごとにサイズを変える方法、面積を正しく拡大する方法を説明します。

このページの内容

Matplotlib の散布図でマーカーサイズを設定するには、Axes.scatter の s 引数を使います。スカラー値はすべての点に同じ公称面積を適用し、1 次元のリストまたは配列は各点に面積を割り当てるため、x と y と同じ長さにする必要があります。

s でマーカーサイズを設定する

Matplotlib の散布図でマーカーサイズを設定するには、Axes.scatter に引数 s を渡します。s=80 のようなスカラーを指定すると、すべての点が同じ公称面積になります。1 次元のリストまたは配列を指定すると点ごとに面積を変えられますが、要素数は x および y と一致しなければなりません。たとえば、1 種類のサイズには ax.scatter(x, y, s=80)、4 点を異なるサイズにするには ax.scatter(x, y, s=[20, 40, 80, 160]) を使います。

観測値ごとにサイズを変える場合は scatter が適しています。関連する plot メソッドでは、線形のマーカー寸法を markersize で指定します。

全点に同じサイズを指定するか点ごとに指定する

次の再現可能な例では、スカラーでサイズを指定したコレクションと、データから計算したサイズを持つコレクションを並べます。同じ値を色にも割り当て、重なったマーカーを見やすくするために alpha を使用します。結果を画像として保存するので、対話型バックエンドがなくても確認できます。

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 11]
values = [1, 2, 4, 7, 11]
sizes = [40 + value * 20 for value in values]

fig, (top, bottom) = plt.subplots(2, 1, figsize=(6, 7), layout="constrained")
uniform = top.scatter(x, y, s=80, color="tab:blue")
varying = bottom.scatter(x, y, s=sizes, c=values, cmap="viridis", alpha=0.75)

top.set(title="s = 80", xlabel="x", ylabel="y")
bottom.set(title="s = [60, 80, 120, 180, 260]", xlabel="x", ylabel="y")
fig.colorbar(varying, ax=bottom)

print("scalar sizes:", uniform.get_sizes().tolist())
print("per-point sizes:", varying.get_sizes().tolist())
fig.savefig("scatter-marker-sizes.webp", dpi=120)

2 つの PathCollection が保持している正確な s の値は次のとおりです。

scalar sizes: [80]
per-point sizes: [60, 80, 120, 180, 260]

1 つのスカラー面積と点ごとの 5 つの面積を比較する 2 つの Matplotlib 散布図。

この変換は 40 から始まり、データ 1 単位につき 20 を加えます。そのため、最小値も見える一方で最大値が軸を覆い尽くしません。実データでは、極端な生の値をそのまま渡さず、説明可能なサイズ範囲の下限と上限を決めてください。色で同じ変数または別の変数を表せますが、色が量を表す場合はカラーバーを付けます。

平方ポイントとデフォルト値を理解する

Matplotlib の scatter リファレンスでは、s の単位は平方ポイント(pt²)です。1 ポイントは 1/72 インチです。デフォルト値は rcParams['lines.markersize'] ** 2 です。したがって s は公称マーカー面積に比例しますが、s=100 が表示上の正確な 10 × 10 ポイントの形を保証するわけではありません。最終的な輪郭は、マーカーパス、図の DPI、縁のスタイルの影響を受けます。

小さなマーカーでは縁の影響が特に大きくなります。linewidths が 0 より大きく、edgecolors が表示される場合、線はマーカー境界を中心に描かれ、有効な外寸は線幅の半分だけ広がります。小さな塗りつぶしマーカーを比較し、縁による見かけのサイズ変化を避けたい場合は、linewidths=0 または edgecolors="none" を使います。

マーカーサイズは表示空間の寸法であり、x 軸や y 軸のデータ単位ではありません。そのため、ズームや軸範囲の変更によってマーカーがデータ座標上の半径を表すようになることはありません。

マーカー面積と線形幅を正しく拡大する

s を 2 倍すると公称面積も 2 倍になります。線形の外寸は面積の平方根にほぼ比例するため、同じ形のマーカーで s を 4 倍にすると幅は約 2 倍になります。これは比例関係を示す視覚上の目安であり、すべてのマーカーパスに対する厳密な幾何学的保証ではありません。

import matplotlib.pyplot as plt

base = 36
area_sizes = [base, 2 * base, 4 * base]

fig, (top, bottom) = plt.subplots(2, 1, figsize=(6, 7), layout="constrained")
top.scatter([1, 2, 3], [1, 1, 1], s=area_sizes, linewidths=0)
top.set(title="s = [36, 72, 144]", yticks=[])
top.set_xticks([1, 2, 3], ["1", "2", "4"])

bottom.scatter([1], [1], s=10**2, linewidths=0)
(line,) = bottom.plot([2], [1], marker="o", linestyle="none", markersize=10,
                     color="tab:orange")
bottom.set(title="scatter: s = 10**2    plot: markersize = 10", xlim=(0.5, 2.5), yticks=[])

print("scatter areas:", area_sizes)
print("plot markersize:", line.get_markersize())
fig.savefig("scatter-size-scaling.webp", dpi=120)

選択した値と Line2D のマーカー寸法は次のとおりです。

scatter areas: [36, 72, 144]
plot markersize: 10.0

散布図の面積係数と scatter の s、plot の markersize を比較する Matplotlib の出力。

scatter(s=...) と plot(markersize=...) を使い分ける

散布図、とりわけ観測値ごとに面積や色を変える場合は scatter(s=...) を使います。すべての節点が同じマーカーサイズの Line2D を描く場合は plot(marker="o", markersize=10) を使います。Matplotlib の plot リファレンスにあるとおり、markersize(または ms)はポイント単位の線形サイズであり、scatter が受け取る平方ポイントの面積ではありません。

円形マーカーについて scatter(s=10**2) と plot(markersize=10) は対応する公称寸法を表しますが、ピクセル単位で完全に一致するとは限りません。正規化されたパス、縁、描画バックエンド、ラスタライズの違いにより、コレクションと線のマーカーは異なる場合があります。

点の数と一致しないサイズ配列を修正する

スカラーでない s は、描画する点と同じ長さでなければなりません。次の例では、完全なトレースバックを出さずに表示できるよう、現行 Matplotlib の診断を捕捉します。

import matplotlib.pyplot as plt

x = [1, 2, 3]
y = [1, 4, 9]

try:
    plt.scatter(x, y, s=[40, 80])
except ValueError as error:
    print(f"{type(error).__name__}: {error}")
else:
    raise AssertionError("a mismatched size array should fail")

Matplotlib 3.11.2 では、出力は次のとおりです。

ValueError: s (size 2) cannot be broadcast to match x and y (size 3)

古い Matplotlib では ValueError の表現が異なることがありますが、修正方法は同じです。3 つの (x, y) 点には 3 つのサイズ値を渡すか、1 つのスカラーサイズを指定してください。