在研究了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)
不幸的是,這是一個非常低級的黑客,這將很容易地(折不繫其他對象,逆轉或日誌軸,...)
由於較高級別的方法不會將emit = False參數轉發到set_xlim(),所以它不可能掛入axes.py中的更高級別的功能避免在set_xlim()和'xlim_changed'回調之間進入無限循環。
此外,似乎沒有統一的方法來確定水平裁剪對象的垂直範圍,因此有單獨的代碼來處理axes.py中的Lines,Patches,Collections等,這些都需要在回調中複製。
在任何情況下,上面的代碼都適用於我,因爲我只在我的情節中有行,並且對緊= True佈局感到滿意。看來只要對axes.py進行一些更改,就可以更加優雅地適應這種功能。
編輯:
我錯了有關無法掛接到更高級別的自動縮放功能。它只需要一組特定的命令來正確分離x和y。我更新了代碼以在y中使用高級自動縮放,這應該使其更加健壯。特別是,tight = False現在可以工作(畢竟看起來好多了),並且反轉/記錄軸應該不成問題。
剩下的一個問題是確定所有類型對象的數據限制,一旦裁剪到特定的x範圍。這個功能應該是真正的內置matplotlib,因爲它可能需要渲染器(例如,如果一個放大得足夠遠,屏幕上只剩下0或1個點,上面的代碼將會中斷)。 Axes.relim()方法看起來很不錯。如果數據已更改,則應重新計算數據限制,但目前僅處理線和修補程序。 Axes.relim()可以有可選參數,用於指定x或y中的窗口。
y軸正在自動縮放,但自動縮放會考慮數據的_full_範圍,而不僅僅是當前縮放窗口中的範圍。在這種情況下,您需要手動設置(半)。 –
@JoeKington:是的,這是發生了什麼事。我可能會認爲這種行爲不符合最小驚訝原則。除了「自動縮放」應該適用於當前可見的數據,而不適用於遠離屏幕的某個區域。 – Stefan