Matplotlib子图行列标签添加及每行右侧标注RMSE的实现方法
标量场廓线图绘制优化方案
核心问题解决方案
问题1:子图行列统一添加标签,避免重复标注拥挤
使用matplotlib子图的轴共享特性即可实现,同一列共享x轴、同一行共享y轴,仅在最底部子图显示x轴刻度标签、最左侧子图显示y轴刻度标签,配合Figure级别的全局轴标签即可完成统一标注:
- 创建子图时指定
sharex='col'、sharey='row'参数 - 保留原有的
fig.supxlabel、fig.supylabel设置全局轴名称
问题2:每行右侧标注对应总RMSE
给每行最后一个子图添加隐藏刻度的右侧Y轴,用该轴的ylabel来展示RMSE数值,位置会自动对齐行居中:
- 遍历每个标量(每一行)时记录行索引
- 该行所有子图绘制完成后,获取行末子图调用
twinx()创建右侧轴 - 隐藏右侧轴的刻度,仅保留RMSE文本标签
优化后完整可运行代码
import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import mean_squared_error # 补充缺失的归一化函数(开启norm参数时需要) def normalize_list(arr): if arr.max() == arr.min(): return arr return (arr - arr.min()) / (arr.max() - arr.min()) dummy_data_A = {'A': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 'B': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 'C': np.random.uniform(low=-3.5, high=3.5, size=(98,501))} dummy_data_B = {'A': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 'B': np.random.uniform(low=-3.5, high=3.5, size=(98,501)), 'C': np.random.uniform(low=-3.5, high=3.5, size=(98,501))} def plot_scalar_profiles(true_var, pred_var, coordinates = ['x','y'], x_position = None, bounds_var = None, norm = False ): n_rows = len(true_var.keys()) n_cols = len(x_position) # 创建共享轴的子图,避免重复标注 fig, axes = plt.subplots(n_rows, n_cols, figsize=(13,10), sharex='col', sharey='row') st = plt.suptitle("标量场廓线对比", fontsize="x-large", y=0.98) scalar_list = list(true_var.keys()) for row_idx, key in enumerate(scalar_list): rmse_list = [] # 计算当前标量的全局x范围,统一该行所有子图x轴 all_profiles = np.concatenate([true_var[key][..., pos] for pos in x_position] + [pred_var[key][..., pos] for pos in x_position]) x_min, x_max = all_profiles.min() * 1.05, all_profiles.max() * 1.05 for col_idx, profile_pos in enumerate(x_position): ax = axes[row_idx, col_idx] # 仅第一行加列标题(x位置) if row_idx == 0: ax.set_title(f"X_pos: {profile_pos}") # 仅第一列加行标题(标量名+Y轴标签) if col_idx == 0: ax.set_ylabel(f"标量{key}\ny Distance (cm)") true_field_star = true_var[key] pred_field_star = pred_var[key] true_profile = true_field_star[...,profile_pos] pred_profile = pred_field_star[...,profile_pos] if norm: true_profile = normalize_list(true_profile) pred_profile = normalize_list(pred_profile) rms = mean_squared_error(true_profile, pred_profile, squared=False) profile_y = range(len(true_profile)) # 线条样式区分真实值和预测值 ax.plot(true_profile, profile_y, label='真实值', color='#1f77b4', linewidth=1.5) ax.plot(pred_profile, profile_y, label='预测值', color='#ff7f0e', linestyle='--', linewidth=1.5) # 子图角落标注当前位置RMSE ax.text(0.95, 0.05, f"RMSE: {rms:.2f}", transform=ax.transAxes, ha='right', va='bottom', bbox=dict(facecolor='white', alpha=0.7)) ax.set_xlim(x_min, x_max) ax.grid(alpha=0.3, linestyle=':') rmse_list.append(rms) # 每行末尾添加总RMSE标注 RMSE = sum(rmse_list) print(f"RMSE {key}: {RMSE}") last_ax = axes[row_idx, -1] rmse_ax = last_ax.twinx() rmse_ax.set_yticks([]) rmse_ax.set_ylabel(f"总RMSE\n{RMSE:.2f}", labelpad=15, fontweight='bold', color='#d62728') # 全局设置 lines, labels = axes[0,0].get_legend_handles_labels() fig.legend(lines, labels, loc = "lower right", bbox_to_anchor=(0.98, 0.02)) fig.supxlabel('u Velocity (m/s)', x=0.5) # 调整布局,给右侧RMSE和顶部标题留空间 fig.tight_layout(rect=[0, 0, 0.92, 0.96]) plt.show() plt.close('all') plot_scalar_profiles(dummy_data_A, dummy_data_B, x_position = [1, 100, 200, 400], bounds_var=None, norm=False)
其他可视化优化建议
- 同一行强制统一x轴范围:避免不同位置的廓线因为轴范围不同造成视觉对比误差
- 线条样式+颜色双区分:真实值用实线、预测值用虚线,即使黑白打印也能清晰区分
- 子图添加单位置RMSE标注:可同时查看全局总误差和每个x位置的局部误差
- 半透明背景的文本标注:避免标注遮挡廓线数据
- 增加浅灰色网格:方便读取廓线对应的数值
内容的提问来源于stack exchange,提问作者Zizi96
相关产品推荐
相关产品推荐

