经过研究matplotlib的axes.py的详细信息,发现没有提供根据数据视图自动缩放轴的方法,因此没有高级方法可以实现我想要的效果。
但是,有“xlim_changed”事件,可以附加回调函数:
import numpy as np
def on_xlim_changed(ax):
xlim = ax.get_xlim()
for a in ax.figure.axes:
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()
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))
a.set_xlim(xlim, emit=False)
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')
a.xlim = xlim
for ax in fig.axes:
ax.callbacks.connect('xlim_changed', on_xlim_changed)
很遗憾,这是一种相当低级的hack方法,很容易出现问题(除了线条,还有反转或对数轴等其他对象...)
似乎无法钩入axes.py中的更高级别功能,因为更高级别的方法不将emit=False参数转发给set_xlim(),这需要避免set_xlim()和“xlim_changed”回调之间进入无限循环。
此外,在水平裁剪对象的垂直范围上确定统一的方法似乎不存在,因此在axes.py中处理Lines、Patches、Collections等需要单独的代码,这些都需要在回调中进行复制。
无论如何,上面的代码对我有效,因为我的绘图中只有线条,并且我对tight=True布局感到满意。看起来只需对axes.py进行少量更改,就可以更优雅地实现此功能。
编辑:
我错了,不能钩入更高级别的自动缩放功能。只需要一组特定的命令就可以正确分离x和y。我更新了代码以在y中使用高级自动缩放,这应该使它更加稳健。特别是,tight=False现在可用(看起来效果好多了),反转/对数轴不应该成为问题。
唯一剩下的问题是确定所有种类对象在裁剪到特定x范围后的数据限制。这个功能真的应该内置于matplotlib中,因为它可能需要渲染器(例如,如果缩放到屏幕上只剩下0或1个点,上面的代码将会出现问题)。Axes.relim()方法看起来是一个很好的候选者。它应该重新计算数据限制如果数据已被更改,但目前仅处理Lines和Patches。可以为Axes.relim()指定窗口中的可选参数x或y。