Matplotlib subplot中sharex与set_xlim参数兼容性问题
问题成因
当sharex=True时,matplotlib会强制绑定所有子图的x轴属性,包括显示范围、刻度定位规则、格式化规则,所有子图的x轴状态会以最后一次修改轴属性的子图设置为准。
原有代码在循环中逐个给三个子图单独调用set_xlim、设置刻度定位器和时间格式化器:obspy的UTCDateTime转pandas时间对象时存在浮点精度误差,哪怕是经过同一参数trim的波形,三个通道单独计算出的limin/limax也会有微秒到毫秒级的差异。共享轴模式下,每次给单个子图设置xlim都会同步修改所有子图的x轴范围,重复设置刻度规则还会触发重绘冲突,最终matplotlib的自动缩放逻辑会判定前两个子图无有效显示内容,将绘图区域压缩为不可见的细线,表现为子图消失。
正确实现方案
- 开启
sharex=True后,不需要在循环中给每个子图重复设置x轴范围、刻度属性,仅需给任意一个子图设置一次,所有共享轴会自动同步配置,matplotlib也会自动隐藏上方子图的x轴刻度标签,无需手动调整 - 提前统一计算一次x轴的上下界,不要逐通道单独计算,从根源避免浮点精度导致的范围冲突
- 不要用
globals()动态创建变量存储标签,直接用列表统一存储即可,代码可读性更高
修正后的核心代码如下:
st.trim(starttime=t0, endtime=(t0+float(dur)), nearest_sample=True) tr3 = st.copy() tr3.filter("bandpass", freqmin=float(flow), freqmax=float(fhigh), corners=4, zerophase=True) # 统一生成通道标签 labels = [] for tr in tr3: labels.append(f"{tr.stats.network}.{tr.stats.station}.{tr.stats.location}.{tr.stats.channel}") # 开启x轴共享 fig, ax = plt.subplots(3,1,sharex=True,sharey=False,figsize=(18,10), dpi=200) # 提前统一计算x轴显示范围,避免逐子图设置的精度误差 x_min = pd.to_datetime(str(tr3[0].stats.starttime)) x_max = pd.to_datetime(str(tr3[0].stats.endtime)) for idx in range(3): ax[idx].plot(tr3[idx].times(("utcdatetime")), tr3[idx].data, 'r', label=labels[idx]) ax[idx].xaxis.grid(True, which='major', color='k', linestyle='dotted', linewidth=0.5) ax[idx].yaxis.grid(True, which='major', color='k', linestyle='dotted', linewidth=0.5) ax[idx].legend(loc='upper left', ncol=4) # 仅需设置一次x轴属性,所有共享子图自动同步 ax[-1].set_xlim(x_min, x_max) ax[-1].xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) ax[-1].xaxis.set_major_locator(mdates.SecondLocator()) fig.suptitle('Évènement du '+str(date)+'T'+str(hr)+' station : '+str(sta))
注:如果三个通道trim后存在极短的时间差,统一使用第一个/最后一个通道的时间范围作为全局x轴范围不会影响波形显示,微秒级的差异肉眼完全无法识别
内容的提问来源于stack exchange,提问作者BenjiBoy
相关产品推荐
相关产品推荐

