変換チュートリアル#

他のグラフィックス パッケージと同様に、Matplotlib は座標系、ユーザーランドデータ 座標系、座標系、座標系、および表示座標系の間を簡単に移動できる変換フレームワークの上に構築されています。プロットの 95% では、内部で発生しているため、これについて考える必要はありません。 Matplotlib が提供する変換を使用するか、独自に作成します (「参考文献」を参照matplotlib.transforms)。以下の表は、いくつかの有用な座標系、各座標系の説明、および各座標系から座標を表示します。「変換オブジェクト」列でaxは、 Axesインスタンスでfigあり、 FigureインスタンスでありsubfigureSubFigureインスタンスです。

座標系

説明

システムからディスプレイへの変換オブジェクト

"データ"

Axes 内のデータの座標系。

ax.transData

「軸」

の座標系 Axes。(0, 0) は軸の左下、(1, 1) は軸の右上です。

ax.transAxes

「サブフィギュア」

の座標系 SubFigure。(0, 0) は部分図の左下、(1, 1) は部分図の右上です。Figure にサブフィギュアがない場合、これは と同じtransFigureです。

subfigure.transSubfigure

"形"

の座標系 Figure。(0, 0) は図の左下、(1, 1) は図の右上です。

fig.transFigure

「フィギュアインチ」

Figureインチ単位の座標系 。(0, 0) は Figure の左下、(width, height) は Figure の右上 (インチ単位) です。

fig.dpi_scale_trans

「xaxis」、「yaxis」

一方の方向にデータ座標を使用し、もう一方の方向に軸座標を使用する混合座標系。

ax.get_xaxis_transform()ax.get_yaxis_transform()

"画面"

出力のネイティブ座標系。(0, 0) はウィンドウの左下、(幅、高さ) は「表示単位」での出力の右上です。

単位の正確な解釈は、バックエンドによって異なります。たとえば、Agg ではピクセル、svg/pdf ではポイントです。

None、 また IdentityTransform()

これらのTransformオブジェクトはソース座標系とターゲット座標系に対してナイーブですが、上記の表で参照されているオブジェクトは、座標系で入力を受け取り、その入力を表示座標系に変換するように構築されています。これが、表示 座標系がNone「変換オブジェクト」列にある理由です。すでに表示座標になっています。命名規則と宛先規則は、利用可能な「標準」座標系と変換を追跡するのに役立ちます。

Transform.inverted変換は、出力座標系から入力座標系への変換を生成するために( を介して) 自分自身を反転する方法も知って います。たとえば、ax.transDataはデータ座標の値を表示座標に変換し、 ax.transData.inversed()matplotlib.transforms.Transform表示座標からデータ座標に変換します。これは、通常は表示スペースで発生するユーザー インターフェイスからのイベントを処理する場合に特に役立ち、マウス クリックまたはキー押下がデータ座標系のどこで発生したかを知りたい場合に役立ちます。

表示座標でアーティストの位置を指定するdpiと、フィギュアのサイズまたはサイズが変わると、相対的な位置が変わる可能性があることに注意してください。オブジェクトの位置やサイズが変わる可能性があるため、印刷時や画面解像度の変更時に混乱が生じる可能性があります。したがって、Axes または Figure に配置されたアーティストは、変換を ;以外に設定するのが最も一般的です。IdentityTransform()を使用してアーティストが Axes に追加されたときのデフォルトは、データ座標で作業して考え、Matplotlib が表示add_artistする変換を処理できる ようにすることです。ax.transData

データ座標#

最も一般的に使用される座標、データ座標系から始めましょう。軸にデータを追加するたびに、Matplotlib は datalimits を更新します。最も一般的にはメソッドset_xlim()set_ylim()メソッドで更新されます。たとえば、次の図では、データ範囲は x 軸で 0 から 10 まで、y 軸で -1 から 1 まで伸びています。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

x = np.arange(0, 10, 0.005)
y = np.exp(-x/2.) * np.sin(2*np.pi*x)

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)

plt.show()
変換チュートリアル

ax.transData以下に示すように、インスタンスを 使用して、データから表示座標系 (単一の点または一連の点)に変換できます。

In [14]: type(ax.transData)
Out[14]: <class 'matplotlib.transforms.CompositeGenericTransform'>

In [15]: ax.transData.transform((5, 0))
Out[15]: array([ 335.175,  247.   ])

In [16]: ax.transData.transform([(5, 0), (1, 2)])
Out[16]:
array([[ 335.175,  247.   ],
       [ 132.435,  642.2  ]])

このinverted() メソッドを使用して、表示からデータ 座標に移動する変換を作成できます。

In [41]: inv = ax.transData.inverted()

In [42]: type(inv)
Out[42]: <class 'matplotlib.transforms.CompositeGenericTransform'>

In [43]: inv.transform((335.175,  247.))
Out[43]: array([ 5.,  0.])

このチュートリアルに従って入力している場合、ウィンドウ サイズまたは dpi 設定が異なると、 表示座標の正確な値が異なる場合があります。同様に、下の図では、ドキュメンテーションの図のサイズのデフォルトが異なるため、点とラベル付けされた表示はおそらく ipython セッションと同じではありません。

x = np.arange(0, 10, 0.005)
y = np.exp(-x/2.) * np.sin(2*np.pi*x)

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)

xdata, ydata = 5, 0
# This computing the transform now, if anything
# (figure size, dpi, axes placement, data limits, scales..)
# changes re-calling transform will get a different value.
xdisplay, ydisplay = ax.transData.transform((xdata, ydata))

bbox = dict(boxstyle="round", fc="0.8")
arrowprops = dict(
    arrowstyle="->",
    connectionstyle="angle,angleA=0,angleB=90,rad=10")

offset = 72
ax.annotate('data = (%.1f, %.1f)' % (xdata, ydata),
            (xdata, ydata), xytext=(-2*offset, offset), textcoords='offset points',
            bbox=bbox, arrowprops=arrowprops)

disp = ax.annotate('display = (%.1f, %.1f)' % (xdisplay, ydisplay),
                   (xdisplay, ydisplay), xytext=(0.5*offset, -offset),
                   xycoords='figure pixels',
                   textcoords='offset points',
                   bbox=bbox, arrowprops=arrowprops)

plt.show()
変換チュートリアル

警告

上記の例のソース コードを GUI バックエンドで実行すると、データ表示の 注釈の 2 つの矢印がまったく同じポイントを指していないことに気付く場合もあります。これは、Figure が表示される前に表示ポイントが計算され、GUI バックエンドが Figure の作成時にわずかにサイズを変更する可能性があるためです。自分で Figure のサイズを変更すると、効果がより顕著になります。これは、ディスプレイ スペースで作業したくない理由の 1 つですが、 に接続して Figure 描画のFigure 座標'on_draw' Eventを更新できます。イベント処理とピッキングを参照してください。

軸の x または y 範囲を変更すると、データ範囲が更新されるため、変換によって新しい表示ポイントが生成されます。ylim だけを変更すると、y 表示座標のみが変更され、xlim も変更すると両方が変更されることに注意してください。これについては、後で について説明するときに詳しく説明し Bboxます。

In [54]: ax.transData.transform((5, 0))
Out[54]: array([ 335.175,  247.   ])

In [55]: ax.set_ylim(-1, 2)
Out[55]: (-1, 2)

In [56]: ax.transData.transform((5, 0))
Out[56]: array([ 335.175     ,  181.13333333])

In [57]: ax.set_xlim(10, 20)
Out[57]: (10, 20)

In [58]: ax.transData.transform((5, 0))
Out[58]: array([-171.675     ,  181.13333333])

軸座標#

データ座標系に次いで、Axesおそらく 2 番目に便利な座標系です。ここで、点 (0, 0) は軸またはサブプロットの左下、(0.5, 0.5) は中央、(1.0, 1.0) は右上です。範囲外のポイントを参照することもできるため、(-0.1, 1.1) は軸の左と上にあります。この座標系は、Axes にテキストを配置する場合に非常に便利です。テキスト バブルを固定位置 (Axes ペインの左上など) に配置し、パンまたはズームしてもその位置を固定したままにすることがよくあるためです。これは、4 つのパネルを作成し、ジャーナルでよく見られるように「A」、「B」、「C」、「D」のラベルを付ける簡単な例です。

fig = plt.figure()
for i, label in enumerate(('A', 'B', 'C', 'D')):
    ax = fig.add_subplot(2, 2, i+1)
    ax.text(0.05, 0.95, label, transform=ax.transAxes,
            fontsize=16, fontweight='bold', va='top')

plt.show()
変換チュートリアル

座標系で線やパッチを作成することもできますが、これは私の経験ではax.transAxes、テキストの配置に使用するよりも役に立ちません。Circleそれにもかかわらず、データ空間にいくつかのランダムなドットをプロットし、軸の中央に中心を置き、半径が軸の 4 分の 1 の半透明をオーバーレイする愚かな例を次に示し set_aspect()ます。 、これは楕円のように見えます。パン/ズーム ツールを使用して移動するか、データの xlim と ylim を手動で変更すると、データが移動するのがわかりますが、円はデータ 座標にないため固定されたままで、常に軸の中心に留まります。 .

fig, ax = plt.subplots()
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y, 'go', alpha=0.2)  # plot some data in data coordinates

circ = mpatches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes,
                       facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
変換チュートリアル

混合変換#

データ座標が混在する混合座標空間での描画 は非常に便利です。たとえば、y データの一部の領域を強調表示するが、データ制限、パンまたはズーム レベルなどに関係なく x 軸全体にわたる水平スパンを作成する場合などです。 . 実際、これらのブレンドされた線とスパンは非常に便利なので、簡単にプロットできるように関数を組み込みました ( 、 、 を 参照)。このトリックは、通常のデカルト座標系で見られるように、分離可能な変換に対してのみ機能しますが、 .axhline()axvline()axhspan()axvspan()PolarTransform

import matplotlib.transforms as transforms

fig, ax = plt.subplots()
x = np.random.randn(1000)

ax.hist(x, 30)
ax.set_title(r'$\sigma=1 \/ \dots \/ \sigma=2$', fontsize=16)

# the x coords of this transformation are data, and the y coord are axes
trans = transforms.blended_transform_factory(
    ax.transData, ax.transAxes)
# highlight the 1..2 stddev region with a span.
# We want x to be in data coordinates and y to span from 0..1 in axes coords.
rect = mpatches.Rectangle((1, 0), width=1, height=1, transform=trans,
                          color='yellow', alpha=0.5)
ax.add_patch(rect)

plt.show()
$\sigma=1 \/ \dots \/ \sigma=2$

ノート

x がデータ座標にあり、y が 座標にあるブレンドされた変換は非常に便利なので、Matplotlib が目盛り、目盛りラベルなどを描画するために内部で使用するバージョンを返すヘルパー メソッドがあります。メソッドはmatplotlib.axes.Axes.get_xaxis_transform()matplotlib.axes.Axes.get_yaxis_transform()です。したがって、上記の例では、への呼び出しを 次のblended_transform_factory()ように置き換えることができますget_xaxis_transform

trans = ax.get_xaxis_transform()

物理座標でのプロット#

オブジェクトをプロット上で特定の物理サイズにしたい場合があります。ここでは、上と同じ円を物理座標で描画します。対話的に行うと、Figure のサイズを変更しても左下隅からの円のオフセットは変更されず、サイズも変更されず、円は軸の縦横比に関係なく円のままであることがわかります。

fig, ax = plt.subplots(figsize=(5, 4))
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y*10., 'go', alpha=0.2)  # plot some data in data coordinates
# add a circle in fixed-coordinates
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
                       facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
変換チュートリアル

図形のサイズを変更すると、円は絶対位置を変更せずにトリミングされます。

fig, ax = plt.subplots(figsize=(7, 2))
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y*10., 'go', alpha=0.2)  # plot some data in data coordinates
# add a circle in fixed-coordinates
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
                       facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
変換チュートリアル

もう 1 つの用途は、軸上のデータ ポイントの周囲に物理的な寸法が設定されたパッチを配置することです。ここでは、2 つの変換を一緒に追加します。1 つ目は楕円の大きさのスケーリングを設定し、2 つ目はその位置を設定します。次に、楕円を原点に配置し、ヘルパー変換を使用して座標系ScaledTranslation の適切な場所に移動します。ax.transDataこのヘルパーは以下でインスタンス化されます:

trans = ScaledTranslation(xt, yt, scale_trans)

ここで、xtytは移動オフセットです。scale_transは、オフセットを適用する前に変換時にxtytをスケーリングする変換です。

以下の変換でのプラス演算子の使用に注意してください。このコードは、まずスケール変換fig.dpi_scale_trans を適用して楕円を適切なサイズにしますが、中心は (0, 0) のままにしてから、データをxdata[0]データydata[0]空間に変換します。

インタラクティブな使用では、軸の範囲がズームによって変更されても、楕円は同じサイズのままです。

fig, ax = plt.subplots()
xdata, ydata = (0.2, 0.7), (0.5, 0.5)
ax.plot(xdata, ydata, "o")
ax.set_xlim((0, 1))

trans = (fig.dpi_scale_trans +
         transforms.ScaledTranslation(xdata[0], ydata[0], ax.transData))

# plot an ellipse around the point that is 150 x 130 points in diameter...
circle = mpatches.Ellipse((0, 0), 150/72, 130/72, angle=40,
                          fill=None, transform=trans)
ax.add_patch(circle)
plt.show()
変換チュートリアル

ノート

変換の順序が重要です。ここでは、まず表示スペースで楕円に正しい寸法が与えられ、次にデータ スペースで正しい位置に移動されます。ScaledTranslation最初に 行った場合は、xdata[0]最初に座標を表示ydata[0]するように変換され( 200 dpi モニターで)、次に、楕円の中心を画面から十分に押し出すことによって座標がスケーリングされます (つまり、)。[ 358.4  475.2]fig.dpi_scale_trans[ 71680.  95040.]

オフセット変換を使用してシャドウ効果を作成する#

のもう 1 つの用途はScaledTranslation、別の変換からオフセットされた新しい変換を作成することです。たとえば、あるオブジェクトを別のオブジェクトに対して少しずらして配置する場合などです。通常、データ座標ではなくポイントやインチなどの物理的な次元でシフトを 行い、さまざまなズーム レベルや dpi 設定でシフト効果が一定になるようにします。

オフセットの 1 つの用途は、影効果を作成することです。最初のオブジェクトと同じオブジェクトをそのすぐ右とそのすぐ下に描画し、zorder を調整して影が最初に描画され、次にオブジェクトが描画されるようにします。その上に影を落とします。

ここでは、上記の使用とは逆の順序 で変換を適用しますScaledTranslation。プロットは最初にデータ座標 ( ) で作成され、次に を使用しておよびポイントax.transDataだけシフトされ ます。(タイポグラフィでは、ポイントは 1/72 インチであり、オフセットをポイントで指定することにより、保存されている dpi 解像度に関係なく、フィギュアは同じように見えます。)dxdyfig.dpi_scale_trans

fig, ax = plt.subplots()

# make a simple sine wave
x = np.arange(0., 2., 0.01)
y = np.sin(2*np.pi*x)
line, = ax.plot(x, y, lw=3, color='blue')

# shift the object over 2 points, and down 2 points
dx, dy = 2/72., -2/72.
offset = transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)
shadow_transform = ax.transData + offset

# now plot the same data with our offset transform;
# use the zorder to make sure we are below the line
ax.plot(x, y, lw=3, color='gray',
        transform=shadow_transform,
        zorder=0.5*line.get_zorder())

ax.set_title('creating a shadow effect with an offset transform')
plt.show()
オフセット変換による影の効果の作成

ノート

dpi とインチのオフセットは、オフセットがmatplotlib.transforms.offset_copy()追加された新しい変換を返す特別なヘルパー関数を で作成する十分に一般的な使用例です。したがって、上記のようにすることもできました:

shadow_transform = transforms.offset_copy(ax.transData,
         fig=fig, dx, dy, units='inches')

変換パイプライン#

このチュートリアルで扱ってきた変換は、データ->表示ax.transDataからの変換パイプラインを構成する 3 つの異なる変換の複合体です。 座標。Michael Droettboom は変換フレームワークを実装し、極座標プロットと対数プロットで発生する非線形投影とスケールを、パンとズーム時に発生する線形アフィン変換から分離するクリーンな API を提供するように注意を払いました。アフィン変換に影響を与える軸をパンおよびズームできるため、ここには効率がありますが、単純なナビゲーション イベントで潜在的に高価な非線形スケールまたは投影を計算する必要がない場合があります。アフィン変換行列を掛け合わせて、それらを 1 ステップで座標に適用することもできます。これは、すべての可能な変換に当てはまるわけではありません。

ax.transData基本的な分離可能な軸Axesクラスでインスタンスを定義する方法は次のとおりです。

self.transData = self.transScale + (self.transLimits + self.transAxes)

transAxes上記のインスタンス については、 Axes 座標で紹介しました。これは、座標軸またはサブプロットの境界ボックスの (0, 0)、(1, 1) コーナーを表示スペースにマップするので、これらの他の 2 つの部分を見てみましょう。

self.transLimitsデータから座標への変換 です。つまり、ビュー xlim と ylim を軸の単位空間にマップします (そしてtransAxes、その単位空間を表示空間に取ります)。ここで実際にこれを見ることができます

In [80]: ax = plt.subplot()

In [81]: ax.set_xlim(0, 10)
Out[81]: (0, 10)

In [82]: ax.set_ylim(-1, 1)
Out[82]: (-1, 1)

In [84]: ax.transLimits.transform((0, -1))
Out[84]: array([ 0.,  0.])

In [85]: ax.transLimits.transform((10, -1))
Out[85]: array([ 1.,  0.])

In [86]: ax.transLimits.transform((10, 1))
Out[86]: array([ 1.,  1.])

In [87]: ax.transLimits.transform((5, 0))
Out[87]: array([ 0.5,  0.5])

この逆変換を使用して、単位 軸座標からデータ座標に戻すことができます。

In [90]: inv.transform((0.25, 0.25))
Out[90]: array([ 2.5, -0.5])

最後の部分はself.transScale属性で、対数軸など、データのオプションの非線形スケーリングを担当します。基本的なMatplotlib軸には線形スケールがあるため、軸が最初に設定されると、これは恒等変換に設定されますが、のような対数スケーリング関数を呼び出す semilogx()か、明示的にスケールを対数に設定するとset_xscale()ax.transScale属性はハンドルに設定されます非線形投影。スケール トランスフォームは、それぞれのxaxisおよび yaxis Axisインスタンスのプロパティです。たとえば、 を呼び出すax.set_xscale('log')と、xaxis はそのスケールを matplotlib.scale.LogScaleインスタンスに更新します。

分離不可能な軸である PolarAxes の場合、考慮すべきもう 1 つの部分、射影変換があります。これtransData matplotlib.projections.polar.PolarAxesは、典型的な分離可能な matplotlib Axes の場合と似ていますが、1 つ追加されてい transProjectionます。

self.transData = self.transScale + self.transProjection + \
    (self.transProjectionAffine + self.transAxes)

transProjectionたとえば、地図データの緯度と経度、または極データの半径とシータなどの空間から、分離可能なデカルト座標系への射影を処理します。パッケージにはいくつかの射影の例がありmatplotlib.projections、Matplotlib は拡張可能な軸と射影をサポートしているため、それらのパッケージのソースを開いて独自の作成方法を確認することをお勧めします。Michael Droettboom は、Hammer 投影軸を作成するための優れたチュートリアルの例を提供しています。カスタム プロジェクションを参照してください 。

スクリプトの合計実行時間: ( 0 分 3.353 秒)

Sphinx-Gallery によって生成されたギャラリー