如何为Matplotlib等高线图添加刻度对齐的次级X/Y轴?
问题描述
我一直尝试使用matplotlib为填充等高线图设置次级y轴和x轴,目标是让次级轴的数值为主轴数值经过自定义函数转换后的结果。我试过secondary_yaxis和secondary_xaxis函数,但对其工作原理感到困惑。以下是测试代码:
import matplotlib.pyplot as plt import numpy as np # the function that I'm going to plot def z_func(x, y): return (1 - (x ** 2 + y ** 3)) * np.exp(-(x ** 2 + y ** 2) / 2) yMax = 2.5 yMin = 1 xMax = yMax xMin = yMin x = np.arange(yMin, yMax, 0.01) y = np.arange(xMin, xMax, 0.01) X, Y = np.meshgrid(x, y) # grid of point Z = z_func(X, Y) # evaluation of the function on the grid fig,ax = plt.subplots() ctr = ax.contourf( X,Y, Z) xticks=np.linspace(xMin,xMax,9) xtickLabels = [fr'{i:.1f}' for i in xticks] ax.set_xticks(ticks=xticks, labels=xtickLabels) ax.set_xlabel('x') yticks = np.linspace( yMin,yMax, 9) ytickLabels = [fr'{i:.1f}' for i in yticks] ax.set_yticks(ticks=yticks, labels=ytickLabels) ax.set_ylabel('y') cbar = fig.colorbar(ctr, ax=ax, location='right') cbar.ax.set_ylabel(r'$z=f(x,y)$') Y = yticks**2 fig.subplots_adjust(left=0.20) def y_forward(y): return 1/y # def forward(y): # return 1/y # def inverse(y): # return y secay=ax.secondary_yaxis('left', functions=(y_forward,y_forward)) secay.spines['left'].set_position(('outward', 40)) secay.set_ylabel(r'Y=1/y') secay.yaxis.set_inverted(True) fig.subplots_adjust(bottom=0.27) def x_forward(x): return 3*x secax=ax.secondary_xaxis('bottom', functions=(x_forward,x_forward)) secax.spines['bottom'].set_position(('outward', 30)) secax.set_xlabel(r'X=3$\times$ x') ax.grid(visible=False) ax.set_title(r'$z=(1-x^2+y^3) e^{-(x^2+y^2)/2}$') fig.tight_layout() plt.show()
我原本以为secondary_yaxis和secondary_xaxis会根据转换函数自动生成刻度,但我希望次级轴的刻度是与主刻度严格对齐的——比如主y轴有9个刻度,次级y轴也应有9个由主刻度经自定义函数计算得到的刻度,且两者位置完全对应。请问能否通过secondary_yaxis和secondary_xaxis实现该需求?还是用twinx和twiny更合适?
解决方案
方法一:正确使用secondary_yaxis/secondary_xaxis
secondary_yaxis的functions参数需要传入两个函数:第一个是主轴转次轴的正向转换函数,第二个是次轴转主轴的逆向转换函数,这样matplotlib才能正确处理轴的映射逻辑。如果要让次级轴和主轴线严格对齐,直接基于主刻度计算次级刻度标签即可,无需依赖自动生成。
修改后的完整代码:
import matplotlib.pyplot as plt import numpy as np # 定义绘图函数 def z_func(x, y): return (1 - (x ** 2 + y ** 3)) * np.exp(-(x ** 2 + y ** 2) / 2) yMax = 2.5 yMin = 1 xMax = yMax xMin = yMin x = np.arange(yMin, yMax, 0.01) y = np.arange(xMin, xMax, 0.01) X, Y = np.meshgrid(x, y) Z = z_func(X, Y) fig, ax = plt.subplots() ctr = ax.contourf(X, Y, Z) # 主X轴设置 xticks = np.linspace(xMin, xMax, 9) ax.set_xticks(ticks=xticks) ax.set_xticklabels([f'{i:.1f}' for i in xticks]) ax.set_xlabel('x') # 主Y轴设置 yticks = np.linspace(yMin, yMax, 9) ax.set_yticks(ticks=yticks) ax.set_yticklabels([f'{i:.1f}' for i in yticks]) ax.set_ylabel('y') # 颜色条设置 cbar = fig.colorbar(ctr, ax=ax, location='right') cbar.ax.set_ylabel(r'$z=f(x,y)$') # 次级Y轴:Y=1/y fig.subplots_adjust(left=0.20) # 定义正向和逆向转换函数 def y_forward(y): return 1/y def y_inverse(y_sec): return 1/y_sec secay = ax.secondary_yaxis('left', functions=(y_forward, y_inverse)) secay.spines['left'].set_position(('outward', 40)) secay.set_ylabel(r'Y=1/y') # 手动设置次级Y轴刻度与主Y轴对齐 secay.set_yticks(yticks) secay.set_yticklabels([f'{y_forward(tick):.2f}' for tick in yticks]) secay.yaxis.set_inverted(True) # 次级X轴:X=3*x fig.subplots_adjust(bottom=0.27) def x_forward(x): return 3*x def x_inverse(x_sec): return x_sec/3 secax = ax.secondary_xaxis('bottom', functions=(x_forward, x_inverse)) secax.spines['bottom'].set_position(('outward', 30)) secax.set_xlabel(r'X=3$\times$x') # 手动设置次级X轴刻度与主X轴对齐 secax.set_xticks(xticks) secax.set_xticklabels([f'{x_forward(tick):.1f}' for tick in xticks]) ax.grid(visible=False) ax.set_title(r'$z=(1-x^2+y^3) e^{-(x^2+y^2)/2}$') fig.tight_layout() plt.show()
关键修改点:
- 给
secondary_yaxis/secondary_xaxis传入完整的正向+逆向转换函数,确保轴的映射逻辑正确 - 手动设置次级轴的
ticks为主轴的ticks,再通过自定义函数计算对应的标签,实现严格对齐
方法二:使用twinx/twiny
如果需要更灵活的控制,twinx/twiny也是可行方案,本质是创建共享轴的孪生轴,手动设置刻度和标签:
示例代码片段(仅展示Y轴部分,X轴同理):
# 孪生Y轴 ax2 = ax.twinx() # 设置孪生轴的位置(向外偏移) ax2.spines['left'].set_position(('outward', 40)) ax2.set_ylabel(r'Y=1/y') # 让孪生轴的刻度位置与主Y轴完全一致 ax2.set_yticks(yticks) # 计算并设置标签 ax2.set_yticklabels([f'{1/tick:.2f}' for tick in yticks]) # 反转轴方向 ax2.invert_yaxis()
两种方法对比
secondary_yaxis/secondary_xaxis:专门为刻度转换场景设计,代码更简洁,自动处理轴的映射关系,适合仅需刻度转换的需求twinx/twiny:自由度更高,适合需要对次级轴做更多自定义操作(比如绘制额外曲线)的场景
内容的提问来源于stack exchange,提问作者ishan_ae
相关产品推荐
相关产品推荐

