はじめに
Matplotlib は、Python で人気のあるデータ可視化ライブラリです。時々、同じグラフに 2 つの異なるスケールのデータをプロットする必要があります。このとき、二次軸の概念が役に立ちます。この実験では、Matplotlib で二次軸を作成する方法を学びます。
VM のヒント
VM の起動が完了したら、左上隅をクリックしてノートブックタブに切り替え、Jupyter Notebook を使って練習しましょう。
時々、Jupyter Notebook が読み込み終わるまで数秒待つ必要がある場合があります。Jupyter Notebook の制限により、操作の検証を自動化することはできません。
学習中に問題に遭遇した場合は、Labby にお問い合わせください。セッション後にフィードバックを提供してください。すぐに問題を解決いたします。
必要なライブラリをインポートする
必要なライブラリをインポートして始めましょう。それは、matplotlib、numpy、およびdatetimeです。
import datetime
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from matplotlib.ticker import AutoMinorLocator
データをプロットする
二次軸の使用方法を示すために、単純なサイン波を作成します。x 軸に度数を使用してサイン波をプロットします。
fig, ax = plt.subplots(layout='constrained')
x = np.arange(0, 360, 1)
y = np.sin(2 * x * np.pi / 180)
ax.plot(x, y)
ax.set_xlabel('angle [degrees]')
ax.set_ylabel('signal')
ax.set_title('Sine wave')
二次軸を作成する
ここでは、二次軸を作成し、x 軸を度数からラジアンに変換します。順方向の関数としてdeg2radを、逆方向の関数としてrad2degを使用します。
def deg2rad(x):
return x * np.pi / 180
def rad2deg(x):
return x * 180 / np.pi
secax = ax.secondary_xaxis('top', functions=(deg2rad, rad2deg))
secax.set_xlabel('angle [rad]')
別の例をプロットする
ここでは、対数対数スケールで波数から波長への変換の別の例をプロットします。この例では、ランダムなスペクトルを使用します。
fig, ax = plt.subplots(layout='constrained')
x = np.arange(0.02, 1, 0.02)
np.random.seed(19680801)
y = np.random.randn(len(x)) ** 2
ax.loglog(x, y)
ax.set_xlabel('f [Hz]')
ax.set_ylabel('PSD')
ax.set_title('Random spectrum')
二次 x 軸を作成する
二次 x 軸を作成し、周波数から周期に変換します。順方向の関数としてone_overを、逆方向の関数としてinverseを使用します。
def one_over(x):
"""Vectorized 1/x, treating x==0 manually"""
x = np.array(x, float)
near_zero = np.isclose(x, 0)
x[near_zero] = np.inf
x[~near_zero] = 1 / x[~near_zero]
return x
## the function "1/x" is its own inverse
inverse = one_over
secax = ax.secondary_xaxis('top', functions=(one_over, inverse))
secax.set_xlabel('period [s]')
二次 y 軸を作成する
データからの即興的な変換で軸を関連付ける例を 3 つ目に作成します。この変換は経験的に導き出されます。この場合、順方向と逆方向の変換関数を、一方のデータセットからもう一方のデータセットへの線形補間に設定します。
fig, ax = plt.subplots(layout='constrained')
xdata = np.arange(1, 11, 0.4)
ydata = np.random.randn(len(xdata))
ax.plot(xdata, ydata, label='Plotted data')
xold = np.arange(0, 11, 0.2)
## 別のデータに基づく座標に x 座標を関連付ける疑似データセット。
## xnew は単調増加でなければならないので、ソートします...
xnew = np.sort(10 * np.exp(-xold / 4) + np.random.randn(len(xold)) / 3)
ax.plot(xold[3:], xnew[3:], label='Transform data')
ax.set_xlabel('X [m]')
ax.legend()
def forward(x):
return np.interp(x, xold, xnew)
def inverse(x):
return np.interp(x, xnew, xold)
secax = ax.secondary_xaxis('top', functions=(forward, inverse))
secax.xaxis.set_minor_locator(AutoMinorLocator())
secax.set_xlabel('$X_{other}$')
複数の軸を作成する
ここでは、最終的な例を作成します。この例では、x 軸ではnp.datetime64を年間日数に変換し、y 軸では摂氏から華氏に変換します。また、3 つ目の y 軸を追加し、位置引数として浮動小数点数を使用して配置します。
dates = [datetime.datetime(2018, 1, 1) + datetime.timedelta(hours=k * 6)
for k in range(240)]
temperature = np.random.randn(len(dates)) * 4 + 6.7
fig, ax = plt.subplots(layout='constrained')
ax.plot(dates, temperature)
ax.set_ylabel(r'$T\ [^oC]$')
plt.xticks(rotation=70)
def date2yday(x):
"""Convert matplotlib datenum to days since 2018-01-01."""
y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
return y
def yday2date(x):
"""Return a matplotlib datenum for *x* days after 2018-01-01."""
y = x + mdates.date2num(datetime.datetime(2018, 1, 1))
return y
secax_x = ax.secondary_xaxis('top', functions=(date2yday, yday2date))
secax_x.set_xlabel('yday [2018]')
def celsius_to_fahrenheit(x):
return x * 1.8 + 32
def fahrenheit_to_celsius(x):
return (x - 32) / 1.8
secax_y = ax.secondary_yaxis(
'right', functions=(celsius_to_fahrenheit, fahrenheit_to_celsius))
secax_y.set_ylabel(r'$T\ [^oF]$')
def celsius_to_anomaly(x):
return (x - np.mean(temperature))
def anomaly_to_celsius(x):
return (x + np.mean(temperature))
## use of a float for the position:
secax_y2 = ax.secondary_yaxis(
1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')
まとめ
この実験では、Matplotlib で二次軸を作成する方法を学びました。二次軸の概念とその作成方法を示すために、さまざまな例を使用しました。