如何消除 plt.xscale('symlog') 绘图时的曲线折点?
结论:这组正数数据优先使用 log 横坐标
将 plt.xscale('symlog') 改为 plt.xscale('log'),可以避开 symlog 在线性区和对数区交界处的斜率变化。这里的 t 从 1 到 10000,全部为正数,适合使用普通对数坐标。
根据给出的公式,norm_pan、norm_tau 和 S_CP 在这个区间内都是光滑函数。如果异常折点出现在 symlog 的线性区边界附近,应优先检查坐标变换。由于没有提供图像,无法确认每个可见弯折都由这一机制引起。
为什么增加采样点没有解决问题?
symlog 在零附近使用线性映射,在较大的正负数区域使用对数映射。默认设置下,两部分在交界处的变换斜率不连续,原本光滑的数据也可能显示出折点。增加采样点无法消除坐标变换自身的斜率跳变。Matplotlib 的 symlog 说明
linthresh 决定线性区的边界,默认值为 2,因此当前数据会跨越 t = 2 这一边界。可以用下面的代码检查实际设置,再观察折点是否位于对应位置:SymmetricalLogScale 源码
ax = plt.gca()
transform = ax.xaxis.get_transform()
print(transform.linthresh)
三条曲线共用横坐标,都会受到这一变换的影响;折点是否显眼,还取决于各条曲线在交界处的斜率。需要区分的是,norm_tau 在 t = tau 附近出现峰值,来自给定公式本身。
修改后的完整代码
下面的代码保留原有计算方式,将横坐标改为 log,并修复输入中损坏的图例字符串和刻度标签。
import numpy as np
import matplotlib.pyplot as plt
t = np.logspace(0, 4, 10**5, base=10)
CP = 400
P_an = 1100
tau = 30
P = CP + P_an / (1 + t / tau)
dP_dPan = 1 / (1 + t / tau)
dP_dtau = (P_an * t) / (tau**2 * (1 + t / tau)**2)
S_CP = CP / P
norm_pan = (dP_dPan - dP_dPan.min()) / (
dP_dPan.max() - dP_dPan.min()
)
norm_tau = (dP_dtau - dP_dtau.min()) / (
dP_dtau.max() - dP_dtau.min()
)
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(t, norm_pan, label=r'$P_{\mathrm{an}}$')
ax.plot(t, norm_tau, label=r'$\tau$')
ax.plot(t, S_CP, label='CP')
ax.vlines(
tau, 0, norm_tau.max(),
linestyles=':', colors='dimgray', alpha=0.4, lw=1.5
)
ax.set_xscale('log')
ax.set_xlim(1, 10001)
ax.set_ylim(0, 1.05)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xticks([1, 10, tau, 100, 1000, 10000])
ax.set_xticklabels(['1', '10', r'$\tau$', '100', '1000', '10000'])
ax.legend(fontsize=9, frameon=False, loc='center right')
fig.tight_layout()
plt.show()
如果需要保留 symlog
对于当前的正数数据,可以把线性区边界移到数据范围以下,例如:
ax.set_xscale('symlog', linthresh=0.1)
这样,t >= 1 的数据都处于对数区,不会经过线性区和对数区的连接点。这个设置只是将连接点移出了数据区间。
如果后续数据包含零或负数,且希望过渡平滑,可以考虑 asinh。它的坐标变换具有平滑梯度;使用前应确认当前 Matplotlib 环境支持该 scale。Matplotlib 的替代方案说明
ax.set_xscale('asinh', linear_width=1.0)
扩大显示范围时要注意什么?
plt.xlim(0.1, 100001) 只修改坐标轴的显示范围。原数组仍然只包含 [1, 10000] 内的数据,两端新增的区域不会自动生成曲线。
如果要计算更宽区间内的数据,需要另外修改 t。此时,norm_pan 和 norm_tau 使用的样本最小值、最大值也可能改变,重新归一化后的数值不一定与原图一致。
备注:内容仅供参考。
