Matplotlib を使った SkewT - logP 図

Beginner

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

はじめに

この実験では、Python の Matplotlib ライブラリを使って SkewT-logP 図を作成する方法を学びます。SkewT-logP 図は、気象学で温度の垂直プロファイルを表示するために一般的に使用されます。非直交な X 軸と Y 軸が関係するため、複雑なプロットです。このプロットを作成するには、Matplotlib の変換とカスタム射影 API を使用します。

VM のヒント

VM の起動が完了したら、左上隅をクリックしてノートブックタブに切り替え、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 軸または下の 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 クラスは、目盛りに 2 つの別々の区間セットを提供し、カスタム目盛りのインスタンスを作成するために使用されます。

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 軸を射影として登録することと、適切な変換を設定することを担当します。必要に応じて、標準のスパインと軸のインスタンスを上書きします。

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)

データを準備する

SkewT - logP 図用のデータを準備します。文字列を読み取るために 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)

SkewT - logP 図を作成する

ここでは、先ほど登録した SkewXAxes 射影を使って SkewT - logP 図を作成します。まず、figure オブジェクトを作成し、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 を使って SkewT - logP 図を作成する方法を学びました。目盛りを描画するための SkewXTick クラス、目盛り用に別個の区間を提供する SkewXAxis クラス、およびスパインを描画する SkewSpine クラスを作成しました。また、SkewXAxes 射影の変換と登録を処理するための SkewXAxes クラスも作成しました。最後に、データを準備して SkewXAxes サブプロットにプロットすることで SkewT - logP 図を作成しました。