使用 Matplotlib 创建斜 T - 对数 P 图

Beginner

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

简介

在本实验中,我们将学习如何使用 Python 中的 Matplotlib 库创建 SkewT-logP 图。SkewT-logP 图常用于气象学中显示温度的垂直剖面。它是一个复杂的图表,因为它涉及非正交的 X 和 Y 轴。我们将使用 Matplotlib 的变换和自定义投影 API 来创建此图表。

虚拟机提示

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

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

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

导入所需库

我们将从导入所需库开始。在这个示例中,我们将使用 Matplotlib、NumPy 和 StringIO。

from contextlib import ExitStack
from matplotlib.axes import Axes
import matplotlib.axis as maxis
from matplotlib.projections import register_projection
import matplotlib.spines as mspines
import matplotlib.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
from io import StringIO
from matplotlib.ticker import (MultipleLocator, NullFormatter, ScalarFormatter)

定义 SkewXTick 类

SkewXTick 类用于在 SkewT-logP 图上绘制刻度。它会检查刻度是需要绘制在 X 轴的上半部分还是下半部分,并相应地设置刻度和网格线的可见性。

class SkewXTick(maxis.XTick):
    def draw(self, renderer):
        with ExitStack() as stack:
            for artist in [self.gridline, self.tick1line, self.tick2line, self.label1, self.label2]:
                stack.callback(artist.set_visible, artist.get_visible())
            needs_lower = transforms.interval_contains(self.axes.lower_xlim, self.get_loc())
            needs_upper = transforms.interval_contains(self.axes.upper_xlim, self.get_loc())
            self.tick1line.set_visible(self.tick1line.get_visible() and needs_lower)
            self.label1.set_visible(self.label1.get_visible() and needs_lower)
            self.tick2line.set_visible(self.tick2line.get_visible() and needs_upper)
            self.label2.set_visible(self.label2.get_visible() and needs_upper)
            super().draw(renderer)

    def get_view_interval(self):
        return self.axes.xaxis.get_view_interval()

定义 SkewXAxis 类

SkewXAxis 类用于为刻度提供两组独立的区间,并创建自定义刻度的实例。

class SkewXAxis(maxis.XAxis):
    def _get_tick(self, major):
        return SkewXTick(self.axes, None, major=major)

    def get_view_interval(self):
        return self.axes.upper_xlim[0], self.axes.lower_xlim[1]

定义 SkewSpine 类

SkewSpine 类计算上 X 轴的单独数据范围,并在那里绘制脊柱(脊线)。它还将此范围提供给 X 轴艺术家以进行刻度和网格线设置。

class SkewSpine(mspines.Spine):
    def _adjust_location(self):
        pts = self._path.vertices
        if self.spine_type == 'top':
            pts[:, 0] = self.axes.upper_xlim
        else:
            pts[:, 0] = self.axes.lower_xlim

定义 SkewXAxes 类

SkewXAxes 类负责将斜 X 轴注册为一种投影,并设置适当的变换。它会根据需要覆盖标准的脊线(spines)和轴实例。

class SkewXAxes(Axes):
    name ='skewx'

    def _init_axis(self):
        super()._init_axis()
        self.xaxis = SkewXAxis(self)
        self.spines.top.register_axis(self.xaxis)
        self.spines.bottom.register_axis(self.xaxis)

    def _gen_axes_spines(self):
        spines = {'top': SkewSpine.linear_spine(self, 'top'),
                  'bottom': mspines.Spine.linear_spine(self, 'bottom'),
                  'left': mspines.Spine.linear_spine(self, 'left'),
                  'right': mspines.Spine.linear_spine(self, 'right')}
        return spines

    def _set_lim_and_transforms(self):
        super()._set_lim_and_transforms()
        rot = 30
        self.transDataToAxes = (self.transScale + self.transLimits + transforms.Affine2D().skew_deg(rot, 0))
        self.transData = self.transDataToAxes + self.transAxes
        self._xaxis_transform = (transforms.blended_transform_factory(
            self.transScale + self.transLimits, transforms.IdentityTransform()) + transforms.Affine2D().skew_deg(rot, 0) + self.transAxes)

    @property
    def lower_xlim(self):
        return self.axes.viewLim.intervalx

    @property
    def upper_xlim(self):
        pts = [[0., 1.], [1., 1.]]
        return self.transDataToAxes.inverted().transform(pts)[:, 0]

注册投影

我们将向 Matplotlib 注册该投影,以便能在绘图中使用它。

register_projection(SkewXAxes)

准备数据

我们将为斜 T - 对数 P 图准备数据。我们会使用 StringIO 模块从字符串读取数据,并使用 NumPy 将其加载到数组中。

data_txt = '''
        978.0    345    7.8    0.8
        971.0    404    7.2    0.2
        946.7    610    5.2   -1.8
     ...
    '''
sound_data = StringIO(data_txt)
p, h, T, Td = np.loadtxt(sound_data, unpack=True)

创建斜 T - 对数 P 图

现在我们将使用之前注册的 SkewXAxes 投影来创建斜 T - 对数 P 图。我们首先创建一个图形对象,并添加一个具有 SkewXAxes 投影的子图。然后,我们将使用 semilogy 函数在图上绘制温度和露点数据。最后,我们将设置 X 轴和 Y 轴的范围和刻度,并显示该图。

fig = plt.figure(figsize=(6.5875, 6.2125))
ax = fig.add_subplot(projection='skewx')

ax.semilogy(T, p, color='C3')
ax.semilogy(Td, p, color='C2')

ax.axvline(0, color='C0')

ax.yaxis.set_major_formatter(ScalarFormatter())
ax.yaxis.set_minor_formatter(NullFormatter())
ax.set_yticks(np.linspace(100, 1000, 10))
ax.set_ylim(1050, 100)

ax.xaxis.set_major_locator(MultipleLocator(10))
ax.set_xlim(-50, 50)

plt.grid(True)
plt.show()

总结

在本实验中,我们学习了如何使用 Matplotlib 的变换和自定义投影 API 创建斜 T - 对数 P 图。我们创建了一个 SkewXTick 类来绘制刻度,一个 SkewXAxis 类来为刻度提供单独的间隔,以及一个 SkewSpine 类来绘制脊线。我们还创建了一个 SkewXAxes 类来处理 SkewXAxes 投影的变换和注册。最后,我们通过准备数据并将其绘制在 SkewXAxes 子图上,创建了一个斜 T - 对数 P 图。