|
|
matplotlib Transformations Tutorial. |
H.Kamifuji . |
他のグラフィックスパッケージと同様に、Matplotlib は座標系、ユーザランドデータ座標系、軸座標系、図座標系、および表示座標系間を簡単に移動できるように変換フレームワークの上に構築されています。 プロットの95%で、これについては考慮する必要はありませんが、カスタムフィギュア生成の限界を押し進めると、これらのオブジェクトを理解して既存のフィギュアを再利用できるようになります 変換 Matplotlib はあなたに利用可能にするか、自分で作成することができます( matplotlib.transforms を参照)。 以下の表は、いくつかの有用な座標系、その座標系で作業するために使用する変換オブジェクト、およびそのシステムの説明をまとめたものです。 変換オブジェクトの列では、ax は Axes のインスタンスであり、fig は Figure のインスタンスです。
上の表のすべての変換オブジェクトは、座標系の入力を受け取り、入力を表示座標系に変換します。そのため、変換座標列には表示座標系に None があります。これはすでに表示座標にあります。変換はまた、ディスプレイから元の座標系に戻るために、逆転する方法も知っています。これは、通常、表示スペースで発生するユーザーインターフェイスからイベントを処理し、データ座標系でマウスクリックまたはキープレスが発生した場所を知りたい場合に特に便利です。 表示座標でオブジェクトを指定すると、図の dpi が変更された場合、その位置が変更されます。これは、オブジェクトが場所とサイズを変更する可能性があるため、画面解像度を印刷または変更するときに混乱を招く可能性があります。したがって、Axes または Figure に配置されたアーティストは、その変換を IdentityTransform() 以外のものに設定するのが最も一般的です。 add_artist を使用している軸上にアーティストが配置されている場合のデフォルトは、変換のための ax.transData です。 |
最も一般的に使用される座標、データ座標系から始めましょう。 Axes にデータを追加するたびに、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 設定を使用すると、表示座標の正確な値が異なる場合があります。 同様に、以下の図では、ドキュメンテーションの Figure のデフォルトサイズが異なるため、ipython セッションの場合と同じではありません。 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() ![]() ![]() 注意軸の 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]) |
データ座標系の後では、軸はおそらく2番目に有用な座標系です。 ここでポイント(0、0)は軸またはサブプロットの左下にあり、(0.5、0.5)は中心、右上は(1.0,1.0)です。 範囲外の点を参照することもできますので、(-0.1、1.1)は軸の左上と上になります。 この座標系は、テキストを軸に配置する際に非常に便利です。固定した場所(たとえば、軸ペインの左上)にテキストバブルを置き、パンまたはズームしたときにその位置を固定しておくことが多いからです。 ここでは、4 つのパネルを作成し、雑誌によく見られるように "A", "B", "C", "D" とラベル付けする簡単な例を示します。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 = 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() ![]() ![]() Axes 座標系で線やパッチを作ることもできますが、これは ax.transAxes を使ってテキストを配置するよりもあまり役に立ちません。 それにもかかわらず、ここでは、データ空間にいくつかのランダムな点をプロットし、軸の中央に半透明の Circle をオーバーレイする愚かな例があります。軸がアスペクト比を保持していない場合 set_aspect() )、これは楕円のように見えます。 パン/ズームツールを使用して移動したり、手動でデータ xlimとylim を変更すると、データの移動が表示されますが、円はデータ座標にないため固定されたままで、常に軸の中央に残ります 。 import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as mpatches 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 軸にまたがる水平スパンを作成するなど、非常に便利です。 Axhspan() 、axvspan() 、axvspan() )を実装するための関数を組み込んでいますが、実際には、これらのブレンドされた行とスパンは、 ブレンド変換を使用してここに展開します。 このトリックは、通常のデカルト座標系のように分離可能な変換でのみ機能しますが、PolarTransform などの分離不可能な変換では機能しません。import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as mpatches 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() ![]() ![]() 注意
trans = ax.get_xaxis_transform() |
ScaledTranslation の別の使用法は、あるオブジェクトを別のオブジェクトに対して相対的にシフトさせるなど、別の変換からオフセットされた新しい変換を作成することです。通常は、データ座標ではなく、ポイントやインチなどの物理的な次元にシフトを適用し、異なるズームレベルとdpi設定でシフト効果が一定になるようにします。 1 つのオフセットの使用は、シャドウエフェクトを作成することです。最初のオブジェクトと同じオブジェクトをその右に、そのすぐ下に描画し、zorder を調整してシャドウが最初に描画され、次にオブジェクトが描画されることを確認しますその上に影を付ける。 ここでは、上の ScaledTranslation の使用と逆の順序で変換を適用します。プロットはまずデータ単位( ax.transData )で作成され、次に fig.dpi_scale_trans を使用して dx ポイントと dy ポイントだけシフトされます。 (タイポグラフィでは、ポイント <https://en.wikipedia.org/wiki/Point_%28typography%29> `_は 1/72 インチです。オフセットをポイント単位で指定すると、Figureはそれが保存される dpi 解像度です)。 import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as mpatches import matplotlib.transforms as transforms 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() ![]() ![]() 注意
hadow_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 coordinates (座標) で導入されました。軸やサブプロットの境界ボックスの(0、0)、(1,1)コーナーを表示スペースにマップしていますので、これらの 2 つの部分を見てみましょう。 self.transLimits は、データから軸の座標に変換する変換です。 つまり、視点 xlim と ylim を軸の単位空間にマッピングします( transAxes はその単位空間を表示空間にとります)。 これを実際に見ることができます In [80]: ax = subplot(111) 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() で対数に設定したりすると Axes が最初に設定されます。 ax.transScale 属性は非線形投影を処理するように設定されています。 スケール変換は、それぞれの x 軸と y 軸の Axis インスタンスのプロパティです。 たとえば、ax.set_xscale('log') を呼び出すと、xaxisはscale を matplotlib.scale.LogScale インスタンスに更新します。 非分離軸の場合、PolarAxes には、投影変換を考慮する必要があります。 transData matplotlib.projections.polar.PolarAxes は、典型的な分離可能な matplotlib Axes のものと似ています。さらに、transProjection を 1 つ追加します。 self.transData = self.transScale + self.transProjection + \ (self.transProjectionAffine + self.transAxes)transProjection は、空間からの投影、例えばマップデータの緯度と経度、または極座標データの半径とシータを分離可能なデカルト座標系に変換します。 matplotlib.projections パッケージにはいくつかの投影例がありますが、Matplotlib は拡張可能な軸と投影をサポートしているので、これらのパッケージのソースを開き、独自の方法を見てください。 Michael Droettboom は、ハンマー投影軸の作成に関する素晴らしいチュートリアルの例を提供しました。 カスタム投影 を参照してください。 |
Transformations Tutorial |
|