2012-06-18 38 views
16

如何創建一個帶有鏈接(共享)x軸的圖的堆棧,在縮放期間自動縮放所有「從」圖的y軸?例如:matplotlib將x軸與自動放大的y軸連接在一起zoom

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() 

當我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軸正在自動縮放,但自動縮放會考慮數據的_full_範圍,而不僅僅是當前縮放窗口中的範圍。在這種情況下,您需要手動設置(半)。 –

+0

@JoeKington:是的,這是發生了什麼事。我可能會認爲這種行爲不符合最小驚訝原則。除了「自動縮放」應該適用於當前可見的數據,而不適用於遠離屏幕的某個區域。 – Stefan

回答

21

在研究了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中的窗口。

+0

+1用於「發現」xlim_changed。我認爲這是無證的,不是嗎? – bmu

+0

@bmu:是的,我偶然發現它正在閱讀axes.py,試圖找到一個地方來吸引。好東西,它在那裏,否則我們會有很多樂趣超載軸和圖... – Stefan

+0

的任何更新?我需要做同樣的事情+想知道現在是否存在更清潔的解決方案。 –