Matplotlib 次坐标轴

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

Matplotlib 是 Python 中一个流行的数据可视化库。有时,我们需要在同一图表上绘制两种不同比例的数据,这时就需要用到次坐标轴的概念。在本实验中,我们将学习如何在 Matplotlib 中创建次坐标轴。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签页,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们会及时为你解决问题。

导入必要的库

我们将首先导入必要的库,即 matplotlibnumpydatetime

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):
    """向量化的 1/x,手动处理 x==0 的情况"""
    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

## 函数“1/x”自身即为其反函数
inverse = one_over

secax = ax.secondary_xaxis('top', functions=(one_over, inverse))
secax.set_xlabel('period [s]')

创建次 y 轴

我们将创建第三个示例,展示如何在一种基于数据的临时变换中关联坐标轴,这种变换是通过经验推导得出的。在这种情况下,我们将正向和反向变换函数设置为从一个数据集到另一个数据集的线性插值。

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 轴上把摄氏度转换为华氏度。我们还将添加第三个 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):
    """将 matplotlib 日期编号转换为自 2018 - 01 - 01 起的天数。"""
    y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
    return y

def yday2date(x):
    """返回 2018 - 01 - 01 之后 *x* 天的 matplotlib 日期编号。"""
    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))

## 使用浮点数表示位置:
secax_y2 = ax.secondary_yaxis(
    1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')

总结

在本实验中,我们学习了如何在Matplotlib中创建次坐标轴。我们使用了各种示例来演示次坐标轴的概念以及如何创建它。