当前位置:首页>python>用 Python 绘制参数敏感性蜘蛛图(含代码)

用 Python 绘制参数敏感性蜘蛛图(含代码)

  • 2026-10-11 08:32:20
用 Python 绘制参数敏感性蜘蛛图(含代码)
案例代码见文末,感谢您关注PFC小姐姐,麻烦您多多对推文点赞、收藏及转发,并衷心希望您多多指教🙏,帮助PFC小姐姐进步提升。

引言

在科研分析中,参数敏感性往往是理解模型行为、筛选关键控制因素和优化研究方案的重要环节。但很多时候,敏感性结果被简单整理成表格或普通柱状图,虽然能看出大小差异,却很难直观反映多个参数之间的整体结构关系,也不方便同时比较不同模型、不同工况下的响应差异。蜘蛛图的优势就在于,它能够把多个参数放在同一个极坐标框架下进行统一表达,使各参数的敏感性高低、不同模型之间的差异以及整体轮廓特征在一张图中直接呈现出来。进一步地,如果再加入热力分层、外圈趋势线或高敏感参数标注,原本相对普通的蜘蛛图就可以被提升为一类信息密度更高、结构更清晰、视觉效果也更强的科研图。下面这组示例图基于同一组模拟参数敏感性数据,分别展示多模型对比版和热力增强版两种表达方式。

1、多模型对比参数敏感性蜘蛛图

下图主要用于展示不同模型或不同方案在同一组参数上的敏感性差异。图中每一个方向对应一个参数,半径大小表示该参数的敏感性指数,不同颜色的轮廓线分别代表不同模型的结果。这样一来,读图时不仅可以判断某一个模型中哪些参数更敏感,也可以直接比较同一个参数在不同模型之间是否一致、差异有多大。图中高亮的方向进一步标出了总体上更值得关注的关键参数,因此这张图的重点不只是“画出多条蜘蛛图”,而是在同一个极坐标结构中同时表达参数强弱、模型差异以及关键控制因子的位置关系。

2、热力增强型参数敏感性蜘蛛图

在基础蜘蛛图的框架上进一步加入了分层热力表达,使图形能够承载更多维度的信息。图中的环带颜色表示不同工况或不同情景下各参数的敏感性强弱,颜色越深通常意味着该参数在对应情景中的影响越大;叠加在其上的黑色轮廓线表示整体平均敏感性水平,而最外层的趋势线则进一步概括了各参数总体的重要性变化。与此同时,图中还对最敏感的几个参数进行了峰值标注,使关键控制因子能够被快速识别出来。

    具体Python如下:

    import osimport numpy as npimport matplotlib.pyplot as pltfrom matplotlib.colors import LinearSegmentedColormap, Normalizefrom matplotlib.lines import Line2D# 0. Output directoryOUTDIR = "sensitivity_spider_output_fixed"os.makedirs(OUTDIR, exist_ok=True)plt.rcParams["font.family"] = "DejaVu Sans"plt.rcParams["axes.unicode_minus"] = False# 1. Simulated dataparam_labels = [    "Elastic E",    "Poisson ν",    "Cohesion c",    "Friction φ",    "Density ρ",    "Perm. k",    "Stress σ",    "Rate v"]schemes = {    "Model A": np.array([0.72, 0.41, 0.88, 0.79, 0.54, 0.46, 0.83, 0.58]),    "Model B": np.array([0.63, 0.52, 0.81, 0.73, 0.60, 0.49, 0.77, 0.66]),    "Model C": np.array([0.82, 0.36, 0.92, 0.85, 0.47, 0.43, 0.88, 0.51]),    "Model D": np.array([0.58, 0.47, 0.76, 0.69, 0.63, 0.55, 0.71, 0.72]),}scheme_colors = {    "Model A": "#355C7D",    "Model B": "#6C5B7B",    "Model C": "#C06C84",    "Model D": "#F67280",}heat_data = np.array([    [0.62, 0.44, 0.85, 0.76, 0.55, 0.42, 0.81, 0.60],    [0.70, 0.48, 0.88, 0.81, 0.50, 0.46, 0.84, 0.57],    [0.78, 0.40, 0.91, 0.86, 0.46, 0.45, 0.89, 0.52],    [0.67, 0.53, 0.83, 0.74, 0.61, 0.50, 0.76, 0.64],    [0.73, 0.46, 0.87, 0.80, 0.57, 0.48, 0.82, 0.59],])mean_profile = heat_data.mean(axis=0)top_idx = np.argsort(mean_profile)[::-1][:3]def close_curve(values):    return np.concatenate([values, [values[0]]])def radar_angles(n):    ang = np.linspace(0, 2 * np.pi, n, endpoint=False)    return np.concatenate([ang, [ang[0]]])angles = radar_angles(len(param_labels))theta = np.linspace(0, 2 * np.pi, len(param_labels), endpoint=False)def style_theta_labels(ax, theta_vals, fontsize=12, pad=12):    """    让极坐标一圈文字往外一点,并根据位置调整对齐方式    """    ax.set_xticks(theta_vals)    ax.set_xticklabels(param_labels, fontsize=fontsize)    ax.tick_params(axis="x", pad=pad)    for label, ang in zip(ax.get_xticklabels(), np.degrees(theta_vals)):        ang = ang % 360        if ang == 0 or ang == 180:            label.set_horizontalalignment("center")        elif 0 < ang < 180:            label.set_horizontalalignment("left")        else:            label.set_horizontalalignment("right")def add_radar_base(ax, rmax=1.0, theta_pad=12):    ax.set_theta_zero_location("N")    ax.set_theta_direction(-1)    ax.set_ylim(0, rmax)    style_theta_labels(ax, theta, fontsize=12, pad=theta_pad)    yticks = np.linspace(0.2, rmax, 5)    ax.set_yticks(yticks)    ax.set_yticklabels([f"{v:.1f}" for v in yticks], fontsize=10, color="gray")    ax.grid(True, linestyle="--", linewidth=0.8, alpha=0.35)    ax.spines["polar"].set_alpha(0.35)def soft_fill(ax, ang, vals, color, alpha=0.16):    ax.fill(ang, vals, color=color, alpha=alpha, zorder=2)# 3. Figure 1def plot_advanced_radar_compare(out_png):    fig = plt.figure(figsize=(10.8, 10.2), dpi=240, facecolor="white")    ax = plt.subplot(111, projection="polar", facecolor="white")    add_radar_base(ax, rmax=1.05, theta_pad=13)    # 背景圆环    for r in [0.2, 0.4, 0.6, 0.8, 1.0]:        ax.plot(            np.linspace(0, 2 * np.pi, 400),            np.full(400, r),            color="#d8dee9",            lw=0.7,            alpha=0.45,            zorder=0        )    # 多模型对比    for name, vals in schemes.items():        vals_c = close_curve(vals)        color = scheme_colors[name]        ax.plot(angles, vals_c, color="white", lw=5.0, alpha=0.95, zorder=2)        ax.plot(angles, vals_c, color=color, lw=2.5, alpha=0.98, zorder=3)        soft_fill(ax, angles, vals_c, color=color, alpha=0.12)        ax.scatter(            angles[:-1], vals,            s=42,            color=color,            edgecolors="white",            linewidths=0.9,            zorder=4        )    # 高敏感参数方向高亮    for idx in top_idx:        ang = theta[idx]        ax.plot([ang, ang], [0, 1.02], color="#f4a261", lw=1.4, alpha=0.55, zorder=1)        ax.scatter([ang], [1.02], s=34, color="#f4a261", edgecolors="white", linewidths=0.8, zorder=5)    ax.set_title(        "Advanced Parameter Sensitivity Spider Chart",        fontsize=22,        pad=28,        fontweight="bold"    )    legend_elements = [        Line2D([0], [0], color=scheme_colors[k], lw=2.8, label=k)        for k in schemes.keys()    ]    ax.legend(        handles=legend_elements,        loc="upper right",        bbox_to_anchor=(1.18, 1.09),        frameon=True,        fontsize=11    )    plt.tight_layout()    plt.savefig(out_png, bbox_inches="tight", facecolor="white")    plt.show()# 4. Figure 2def plot_advanced_radar_heat(out_png):    fig = plt.figure(figsize=(11.2, 10.8), dpi=260, facecolor="white")    ax = plt.subplot(111, projection="polar", facecolor="white")    ax.set_theta_zero_location("N")    ax.set_theta_direction(-1)    # 外面多留一些空间给 Peak 标注    ax.set_ylim(0, 1.55)    style_theta_labels(ax, theta, fontsize=12, pad=13)    ax.set_yticks([0.3, 0.6, 0.9, 1.2])    ax.set_yticklabels(["0.3", "0.6", "0.9", "1.2"], fontsize=10, color="gray")    ax.grid(True, linestyle="--", linewidth=0.8, alpha=0.30)    ax.spines["polar"].set_alpha(0.35)    cmap = LinearSegmentedColormap.from_list(        "radar_heat",        ["#dbe4ee", "#7fb3e6", "#5f8ee6", "#8b5ce6", "#d94696"]    )    norm = Normalize(vmin=heat_data.min(), vmax=heat_data.max())    n_layers = heat_data.shape[0]    n_params = heat_data.shape[1]    r0 = 0.18    dr = 0.17    width = 2 * np.pi / n_params    # 分层热力环带    for i in range(n_layers):        bottom = r0 + i * dr        for j in range(n_params):            val = heat_data[i, j]            color = cmap(norm(val))            ax.bar(                theta[j],                height=dr * 0.88,                width=width * 0.96,                bottom=bottom,                color=color,                edgecolor="white",                linewidth=1.0,                align="center",                alpha=0.95,                zorder=2            )    # 平均蜘蛛轮廓    mean_r = 0.20 + 0.95 * mean_profile    mean_r_c = close_curve(mean_r)    ax.plot(angles, mean_r_c, color="#1f2937", lw=2.8, alpha=0.97, zorder=5)    ax.fill(angles, mean_r_c, color="#1f2937", alpha=0.05, zorder=4)    ax.scatter(        angles[:-1], mean_r,        s=42,        color="#111827",        edgecolors="white",        linewidths=0.8,        zorder=6    )    # 外侧趋势线    prof_norm = (mean_profile - mean_profile.min()) / (mean_profile.max() - mean_profile.min() + 1e-12)    outer_base = 1.20    outer_curve = outer_base + 0.13 * prof_norm    outer_curve_c = close_curve(outer_curve)    ax.plot(angles, outer_curve_c, color="#17b2cf", lw=2.4, alpha=0.95, zorder=7)    ax.fill(angles, outer_curve_c, outer_base, color="#17b2cf", alpha=0.10, zorder=3)    # 峰值标注:只保留 Peak 1/2/3,并手动错开    peak_rank = np.argsort(mean_profile)[::-1][:3]    # 每个峰值的角度偏移(度)、半径偏移、对齐方式    # 用来避免和外圈参数标签打架    peak_offsets = [        (-12, 0.10, "right"),   # Peak 1        ( 10, 0.12, "left"),    # Peak 2        ( 16, 0.11, "left"),    # Peak 3    ]    for n, idx in enumerate(peak_rank):        ang = theta[idx]        rr = outer_curve[idx]        d_ang_deg, d_r, ha = peak_offsets[n]        text_ang = ang + np.deg2rad(d_ang_deg)        text_r = rr + d_r        ax.scatter(            [ang], [rr],            s=60,            color="#f59e0b",            edgecolors="white",            linewidths=0.9,            zorder=8        )        ax.annotate(            f"Peak {n+1}",            xy=(ang, rr),            xytext=(text_ang, text_r),            textcoords="data",            fontsize=11,            color="black",            ha=ha,            va="center",            arrowprops=dict(                arrowstyle="->",                color="black",                lw=0.9,                alpha=0.9,                shrinkA=0,                shrinkB=4            ),            zorder=9        )    ax.set_title(        "Heat-Enhanced Parameter Sensitivity Spider Chart",        fontsize=22,        pad=30,        fontweight="bold"    )    sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)    sm.set_array([])    cbar = plt.colorbar(sm, ax=ax, pad=0.08, fraction=0.045)    cbar.set_label("Sensitivity Index", fontsize=12)    cbar.ax.tick_params(labelsize=10)    plt.tight_layout()    plt.savefig(out_png, bbox_inches="tight", facecolor="white")    plt.show()def main():    out1 = os.path.join(OUTDIR, "sensitivity_spider_advanced_1_fixed.png")    out2 = os.path.join(OUTDIR, "sensitivity_spider_advanced_2_fixed.png")    plot_advanced_radar_compare(out1)    plot_advanced_radar_heat(out2)if __name__ == "__main__":    main()

    特别声明:

    以上代码与文案均为网上资料整合而成,仅供广大同行们参考学习,如有侵权请联系删除。

    如有其他需要,欢迎关注我的咸鱼号:pfc小姐姐

    最新文章

    随机文章