matplotlib の axes.py の詳細を調べたところ、データのビューに基づいて軸を自動スケーリングするための規定がないように見えるため、私が望んでいたことを達成するための高レベルの方法はありません。
ただし、コールバックをアタッチできる「xlim_changed」イベントがあります。
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)
残念ながら、これは非常に低レベルのハックであり、簡単に壊れてしまいます (Lines 以外のオブジェクト、反転軸、対数軸など)。
より高いレベルのメソッドは、set_xlim() と「xlim_changed」コールバック。
さらに、水平方向にトリミングされたオブジェクトの垂直方向の範囲を決定する統一された方法はないようです。そのため、axes.py で Lines、Patches、Collections などを処理する別のコードがあり、これらはすべてコールバックで複製する必要があります。 .
いずれにせよ、私のプロットには行しかなく、タイト = True レイアウトに満足しているので、上記のコードはうまくいきました。Axes.py を少し変更するだけで、この機能をより洗練されたものにできるようです。
編集:
より高いレベルの自動スケール機能に接続できないというのは間違っていました。x と y を適切に分離するには、特定のコマンド セットが必要です。y で高レベルの自動スケーリングを使用するようにコードを更新しました。これにより、大幅に堅牢になるはずです。特に、tight=False が機能するようになり (結局のところ、はるかに見栄えが良くなりました)、逆/対数軸は問題になりません。
残っている 1 つの問題は、特定の x 範囲にトリミングされた後、すべての種類のオブジェクトのデータ制限を決定することです。この機能は、レンダラーが必要になる場合があるため、実際には組み込みの matplotlib である必要があります (たとえば、上記のコードは、画面に 0 または 1 ポイントしか残らないほどズームインすると壊れます)。メソッド Axes.relim() は良い候補のようです。データが変更された場合、データ制限を再計算することになっていますが、現在は Lines と Patches のみを処理します。x または y でウィンドウを指定する Axes.relim() へのオプションの引数が存在する可能性があります。