如何修复使用plt.xscale('symlog')时norm_pan与norm_tau曲线出现的折角绘图伪影?
解决Matplotlib Symlog刻度下曲线折角伪影的问题
嘿,我遇到过类似的坑!你观察到的折角伪影其实和symlog刻度的核心特性有关,咱们一步步拆解解决:
问题根源
symlog(对称对数刻度)是专门为包含0或正负数值的场景设计的——它默认会把绝对值小于linthresh(默认值为1)的区间用线性刻度展示,大于linthresh的区间用对数刻度。你的数据t从1开始,但当你扩大x轴范围到0.1时,0.1到1的区间会切换成线性刻度,而1以上是对数刻度。
你用np.logspace生成的是对数间隔的点,这些点在symlog轴的线性段和对数段的像素映射逻辑完全不同,这才导致了视觉上的折角伪影——哪怕你用了1e5个点也没用,因为这根本不是点密度的问题,是刻度转换的逻辑冲突。
而且关键是:你的x轴数据全是正数,完全没必要用symlog啊!
解决方案
方案1:改用普通对数刻度(最推荐)
既然数据都是正的,直接用plt.xscale('log')替代symlog就能彻底解决伪影。普通对数刻度对所有正数都采用统一的对数映射,没有线性过渡段,曲线会完全平滑。
修改后的完整代码:
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()) plt.figure(figsize=(6,4)) plt.plot(t, norm_pan, label=r'$\text{P}_\text{an}$') plt.plot(t, norm_tau, label=r'$\tau$') plt.plot(t, S_CP, label="CP") plt.vlines(tau, 0, max(norm_tau), linestyles=':', colors="dimgray", alpha=.4, lw=1.5) # 替换为普通对数刻度 plt.xscale('log') plt.xlim(1, 10001) plt.ylim(0, 1.05) ax = plt.gca() 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]) plt.legend(fontsize=9, frameon=False, loc='center right') plt.tight_layout() plt.show()
方案2:如果必须保留Symlog刻度
如果你后续需要扩展数据范围到包含0或负数,那可以调整symlog的参数,让你的数据全部落在对数区间内:
# 设置linthresh为小于数据最小值的数(比如0.01),让1以上的所有数据都用对数刻度 plt.xscale('symlog', linthresh=0.01)
这样就避开了线性段和对数段的过渡,伪影自然会消失。
额外说明
你提到CP曲线可能也有伪影,确实如此——只是它的斜率很小,在symlog过渡处的视觉变化不明显,但改用log刻度后,潜在的伪影也会被完全消除。
内容的提问来源于stack exchange,提问作者Márton Horváth
相关产品推荐
相关产品推荐

