2012-06-18 12 views
16

ズーム中にすべての "スレーブ"プロットのy軸をオートスケールするリンク(共有)x軸を持つプロットのスタックを作成するにはどうすればよいですか?例えば:matplotlibは、ズーム時に自動スケールされたy軸を持つx軸にリンクします

import matplotlib.pyplot as plt 
fig = plt.figure() 
ax1 = fig.add_subplot(211) 
ax2 = fig.add_subplot(212, sharex=ax1) 
ax1.plot([0,1]) 
ax2.plot([2,1]) 
plt.show() 

IはAX1ズームイン、これは同様に(これまでのところ良い)AX2のx軸を更新し、私は今も目に見えるデータの範囲に基づいて、自動スケールするAX2のY軸を望ん。すべての自動スケール設定がオンになっています(デフォルト)。 ax2を作成した後に自動スケール設定を手動で設定するのに役立たなかった:

ax2.autoscale(enable=True, axis='y', tight=True) 
ax2.autoscale_view(tight=True, scalex=False, scaley=True) 

print ax2.get_autoscaley_on() 
-> True 

私は何かを見逃しましたか?

+2

y軸は自動スケールされていますが、オートズームでは現在のズームウィンドウの範囲だけでなく、データの_フル範囲も考慮されます。この場合、手動で設定する必要があります(半)。 –

+0

@JoeKington:はい、これが起こります。私は、この行動が最も驚異的な原理に対応していないと主張するかもしれない。現在表示されているデータには「オートスケール」が適用され、画面外の一部の領域には適用されません。 – Stefan

答えて

21

matplotlibのaxes.pyの詳細を調べた結果、データのビューに基づいて軸を自動スケールする手段がないように見えるので、私が望むものを達成するための高度な方法はありません。

はしかし、「xlim_changed」イベントは、1つのコールバックを添付することができますしている、があります。

import numpy as np 

def on_xlim_changed(ax): 
    xlim = ax.get_xlim() 
    for a in ax.figure.axes: 
     # shortcuts: last avoids n**2 behavior when each axis fires event 
     if a is ax or len(a.lines) == 0 or getattr(a, 'xlim', None) == xlim: 
      continue 

     ylim = np.inf, -np.inf 
     for l in a.lines: 
      x, y = l.get_data() 
      # faster, but assumes that x is sorted 
      start, stop = np.searchsorted(x, xlim) 
      yc = y[max(start-1,0):(stop+1)] 
      ylim = min(ylim[0], np.nanmin(yc)), max(ylim[1], np.nanmax(yc)) 

     # TODO: update limits from Patches, Texts, Collections, ... 

     # x axis: emit=False avoids infinite loop 
     a.set_xlim(xlim, emit=False) 

     # y axis: set dataLim, make sure that autoscale in 'y' is on 
     corners = (xlim[0], ylim[0]), (xlim[1], ylim[1]) 
     a.dataLim.update_from_data_xy(corners, ignore=True, updatex=False) 
     a.autoscale(enable=True, axis='y') 
     # cache xlim to mark 'a' as treated 
     a.xlim = xlim 

for ax in fig.axes: 
    ax.callbacks.connect('xlim_changed', on_xlim_changed) 

は残念ながら、これは逆に簡単に壊れるかなり低レベルのハック、(株以外のオブジェクトであり、 ...)

上位レベルのメソッドは、emit = False引数をset_xlim()に転送しないため、axes.pyの上位レベルの機能にフックすることはできません。これは必須ですset_xlim()と 'xlim_changed'コールバックの間に無限ループが入り込まないようにする必要があります。

さらに、水平に切り取られたオブジェクトの垂直範囲を判断する統一された方法はないようです。したがって、axes.pyのLines、Patches、Collectionsなどを処理する別々のコードがあります。コールバックで複製されます。

いずれにしても、私のプロットには線しかないので上記のコードは私のために働きました。私はtight = Trueレイアウトに満足しています。 axes.pyをほんの少し変更するだけで、この機能をよりエレガントに受け入れることができます。

編集:

私はより高いレベルのオートスケール機能にフックすることができない程度間違っていました。 xとyを適切に区切るには、特定のコマンドセットが必要です。 yで高水準の自動拡大を使用するようにコードを更新しました。これにより、大幅にロバストになるはずです。特に、tight = Falseが機能するようになりました(やはりはるかに良く見えます)、逆/ログ軸は問題にならないはずです。

残りの問題の1つは、特定のxエクステントにトリミングされたすべての種類のオブジェクトのデータ制限の決定です。レンダラーが必要な場合があるので、この機能は実際にはmatplotlibに組み込まれていなければなりません(例えば、画面上に0または1ポイントしか残らない場合は上のコードが壊れます)。 Axes.relim()メソッドは良い候補のように見えます。データが変更されたが、現在は線とパッチのみを処理している場合、データの限界を再計算することになっています。 Axes.relim()にオプションの引数を指定すると、xまたはyのウィンドウを指定できます。

+0

"xlim_changed"を "発見"するために+1。私はこれが文書化されていないと思うのですか? – bmu

+0

@bmu:はい、私はaxes.pyを読んでそれに遭遇して、フックする場所を見つけようとしました。そこには良いことがあります。そうでなければ、AxesとFigureのオーバーロードが楽しくなるでしょう... – Stefan

+0

私は同じことをする必要があります+今よりクリーンなソリューションが存在するかどうか疑問に思っていました。 –

関連する問題