HOWTO · Matplotlib
Matplotlib で対数軸をプロットする方法
set_yscale()、semilogy()および loglog()関数を使用して、Matplotlib で対数軸をプロットできます。
このページの内容
set_xscale() または set_yscale() を使うと、x軸とy軸のスケールをそれぞれ独立して設定できます。両方の軸を対数スケールにするには loglog() を使います。semilogx() と semilogy() は、それぞれ x軸または y軸に対数スケールを適用するヘルパー関数です。 対数軸は目盛りを10の累乗ごとに配置するため、数桁にわたるデータでも、線形軸の最も小さい桁の範囲に密集することなく読み取れます。
以下のコード例はすべて正常に実行されました。
set_xscale() または set_yscale() 関数
set_xscale("log") を呼び出すと x軸が、set_yscale("log") を呼び出すと y軸が対数スケールになります。両方を呼び出せば、両軸を対数スケールにできます。 2つの呼び出しは独立しているため、片方の軸だけを変換し、もう片方を線形のままにできます。
x軸だけを対数スケールにするには、通常どおりデータをプロットしてから、Axes に対して set_xscale("log") を呼び出します。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_xscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
出力結果から、変わったのは x軸だけであることが分かります(x-axis: log, y-axis: linear)。値自体は10倍ずつ増えているのに、1、10、100、1000 の目盛りは等間隔になっています。
y軸だけを対数スケールにするには、代わりに set_yscale("log") を呼び出し、x軸には触れません。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_yscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
今度は出力が x-axis: linear, y-axis: log となり、等間隔の目盛りは y軸側に付きます。
pyplot のステートマシンインターフェースでは、現在の Axes に作用するトップレベル関数 plt.xscale() と plt.yscale() で同じ変更ができます。同じプロットの yscale() 版は次のとおりです。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
plt.plot(x, y, marker="o")
plt.yscale("log")
plt.xlabel("x")
plt.ylabel("y")
ax = plt.gca()
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close()
結果は同じ対数 y軸です。明示的な ax 参照を保持するプログラムでは、対象の Axes が呼び出しの中で名前で指定されるため、現在の図から暗黙に決まる pyplot 形式より Axes オブジェクトのメソッドのほうが分かりやすいのが普通です。
この一連の例では、正の数値データをプロットし、両軸を対数スケールに設定します。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
出力は x-axis: log, y-axis: log となり、等しい比率が両軸で等しい距離を占めます。
標準の log スケールで表せるのは正の値です。データに正負の値が含まれる場合は、対象の軸に symlog を使います。たとえば ax.set_yscale("symlog") とすると、ゼロの両側にある値を表示できます。 最後のセクションでは、このケースを検証済みの例で改めて取り上げます。
semilogx() または semilogy() 関数
x軸を対数スケール、y軸を線形スケールにする場合は semilogx() を使います。y軸を対数スケール、x軸を線形スケールにする場合は semilogy() を使います。どちらの関数も引数として x と y のデータを受け取るため、プロットの呼び出しで適用するスケールが明確になります。両軸を対数スケールにするには、loglog() を使うか、同じ Axes に semilogx() と semilogy() の両方を適用します。
次の semilogx() 呼び出しは、データのプロットと対数 x軸の設定を一度に行います。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
fig, ax = plt.subplots()
ax.semilogx(x, y, marker="o", label="logarithmic x")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
このヘルパーはプロットした線を返し、その後 Axes は x-axis: log, y-axis: linear を報告します。これは第1節の明示的な set_xscale("log") 呼び出しとまったく同じです。
2つのヘルパーを同じ Axes に組み合わせると、両軸が対数スケールになります。スケールは個々の線ではなく Axes に属することに注意してください。semilogy() が y軸を設定すると、そのスケールは、先に semilogx() で描いたものを含め、Axes が保持するすべての系列に適用されます。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
y2 = [3, 8, 30, 120]
fig, ax = plt.subplots()
ax.semilogx(x, y, marker="o", label="first series")
ax.semilogy(x, y2, marker="s", label="second series")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
両方の系列が対数の x軸と y軸上に描かれ、出力が最終状態 x-axis: log, y-axis: log を裏付けます。
loglog() 関数
両軸を対数スケールにするには loglog() を使います。この関数は指定した x と y のデータをプロットし、両軸を標準の対数スケールに設定します。次は、正の数値を使った一連の例です。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
fig, ax = plt.subplots()
ax.loglog(x, y, marker="o")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
レンダリング結果は第1節の set_xscale("log") と set_yscale("log") の組み合わせと一致します。loglog() はスケールの選択をプロット呼び出し自体に取り込んだだけです。
非正の値と無効なスケール名
対数軸の利用者がよく驚く点が2つあります。見た目のおかしいグラフをデバッグする前に知っておくと役立ちます。第一に、スケール名は即座に検証されます。ax.set_yscale("logg") のようなタイプミスは ValueError: 'logg' is not a valid value for scale. Did you mean one of: 'log', 'logit', 'symlog'? を発生させます(メッセージは Matplotlib 3.11 で逐語的に取得)。エラーが受け付ける値を示すため、タイプミスは線形軸を黙って生成するのではなく、その場で失敗します。
第二に、対数スケールはゼロや負の値を表現できず、このケースではエラーは発生しません。Matplotlib は非正の点を黙って破棄します。それらの点は描画からマスクされ、そこを通るはずの線分は消え、軸の範囲は正の値だけをカバーします。次の例は y = [2, 0, -5, 80] を対数 y軸にプロットします。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 0, -5, 80]
fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_yscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
呼び出しは正常に完了して y-axis: log を報告しますが、表示できるのは y = 2 と y = 80 の点だけです。検証済みの実行では、結果の y軸範囲は約1.66から96.2で、正のデータだけをカバーしていました。軸を log に切り替えた後に系列の点が減ったように見える場合は、プロットコードを疑う前に、データにゼロや負の値が含まれていないか確認してください。
データに実際に符号付きの値が含まれる場合、適切な代替は symlog(対称対数)スケールです。ゼロから離れた範囲では対数スケール、ゼロ周辺の小さな範囲では線形スケールを適用するため、すべての点が表示されたままになります。
import matplotlib.pyplot as plt
x = [1, 10, 100, 1000]
y = [2, 0, -5, 80]
fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_yscale("symlog")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)
0 と -5 を含む4点すべてが symlog の y軸上にプロットされます。値の符号自体が情報を持つ場合はこちらを使い、個数、サイズ、物理的な測定値など、本来正であるデータには通常の log スケールを使ってください。