雷达图(Radar Chart),也被称为蜘蛛图(Spider Chart),是一种非常有效的多维度数据可视化工具。在机器学习模型比较、性能评估等场景中,雷达图能够直观地展示多个模型在不同指标上的表现,快速识别各模型的优势和劣势。
本文将详细介绍如何使用Python和matplotlib库,从CSV数据文件开始,一步步构建一个专业、美观的多模型比较雷达图。将实现以下功能:
同时每个单图也会自动保存!!
在开始绘制之前,需要导入必要的库,并设置matplotlib的全局参数。这些设置将确保生成的图表符合学术期刊的发表标准,特别是Nature等顶级期刊对图表格式的要求。
首先,导入核心库:numpy用于数值计算,pandas用于数据处理,matplotlib用于绘图。然后,配置matplotlib的全局参数,包括字体、字号、线条宽度等。这些设置将应用到所有后续绘制的图表中。
import numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom matplotlib import rcParams# =========================# 0. 基础设置 (Nature 风格)# =========================config = {"font.family": 'serif',"font.serif": ['Times New Roman'], "mathtext.fontset": 'stix',"font.size": 12,"axes.linewidth": 1.0,"figure.dpi": 300,}rcParams.update(config)关键设置说明:
字体设置:使用serif字体族,并指定Times New Roman作为首选字体。这是学术期刊常用的字体,具有专业、正式的外观。
数学公式字体:mathtext.fontset设置为stix,确保数学公式与正文字体风格一致。
字号和线条:基础字号设为12pt,坐标轴线宽设为1.0,这些都是期刊发表的标准设置。
分辨率:figure.dpi设为300,确保导出的图片具有足够的分辨率,适合印刷和在线发表。
数据是可视化的基础。设计为读取CSV格式的数据文件,其中第一列是模型名称(Model列),其余列是各种评估指标(如Accuracy、Precision、Recall等)。
这个函数的设计非常简洁高效:它读取CSV文件,将数据分为模型名称列表、指标名称列表和数值矩阵三部分。这种分离式的数据结构使得后续处理更加清晰和灵活。
# =========================# 1. 数据读取# =========================CSV_NAME = "model_performance.csv"def_read_table(csv_path):# 三行读取:Model列=模型名,其余列=指标 data = pd.read_csv(csv_path) X = data.drop(columns=["Model"]) y = data["Model"]return y.astype(str).tolist(), X.columns.tolist(), X.astype(float).to_numpy()函数解析:
数据分离:使用drop(columns=["Model"])将模型名称列分离出来,剩余的列就是各个评估指标。
类型转换:模型名称转换为字符串列表,指标名称直接获取列名,数值数据转换为numpy数组以便后续计算。
返回值:返回三个值,分别是模型名称列表、指标名称列表和数值矩阵。这种设计使得函数调用非常直观。
这是整个代码中最关键也最复杂的函数之一。在极坐标图中,标签的旋转角度计算需要特别小心,因为matplotlib的极坐标系统有theta_offset(角度偏移)和theta_direction(方向)两个参数,这些参数会影响标签的实际显示位置。
如果简单地使用原始角度,标签可能会出现倒置或方向错误的问题。函数通过计算"显示角度"(考虑了偏移和方向),然后让文字沿着圆环的切线方向排列,并在下半圆自动翻转,确保文字始终可读。
def_draw_ring_label(ax, text, angle_rad, radius, fontsize=12):# 需要用"显示角度"(考虑 theta_offset / theta_direction),否则会出现 AUC 等标签朝向不对 display_theta = ax.get_theta_direction() * angle_rad + ax.get_theta_offset() angle_deg = (np.degrees(display_theta) + 360) % 360# 让文字沿切线方向放置;并在下半圆翻转,保证可读 rotation = angle_deg - 90 rotation = (rotation + 180) % 360 - 180# 归一化到 [-180, 180]if rotation < -90or rotation > 90: rotation += 180 rotation = (rotation + 180) % 360 - 180 ax.text( angle_rad, radius, text, ha="center", va="center", rotation=rotation, rotation_mode="anchor", fontsize=fontsize, fontweight="bold", color="black", zorder=5, )算法详解:
显示角度计算:display_theta = ax.get_theta_direction() * angle_rad + ax.get_theta_offset()
角度归一化:将角度转换为0-360度的范围,避免负角度带来的问题
切线方向计算:rotation = angle_deg - 90
可读性优化:如果旋转角度在-90到90度之外,文字会倒置,此时需要翻转180度,确保文字始终正向可读
文本绘制:使用rotation_mode="anchor"确保文字围绕锚点旋转,而不是围绕中心点
在设置坐标轴刻度时,经常需要将数值四舍五入到指定的小数位数。Python内置的round函数使用的是"银行家舍入法"(round half to even),这在某些情况下可能不符合预期。实现了一个标准的四舍五入函数,使用Decimal模块确保精度。
def_round_half_up(x: float, ndigits: int = 2) -> float:from decimal import Decimal, ROUND_HALF_UPif ndigits <= 0: q = Decimal("1")else: q = Decimal("0." + "0" * (ndigits - 1) + "1")return float(Decimal(str(x)).quantize(q, rounding=ROUND_HALF_UP))函数特点:
Decimal类型避免浮点数精度问题ROUND_HALF_UP确保0.5总是向上舍入这是整个代码的核心部分,plot_radar函数负责绘制完整的雷达图。这个函数设计得非常全面,包含了雷达图的所有要素:外环标签、网格线、数据系列、图例等。
函数接受多个参数,包括模型名称、指标名称、数值矩阵、线条颜色、外环颜色等。这种参数化的设计使得函数具有很高的灵活性。
defplot_radar( ax, model_names: list[str], metric_names: list[str], values: np.ndarray, line_colors_local: list[str], ring_colors_local: list[str], *, annotate: str | None = None, legend: bool = True, legend_fontsize: float = 11, label_fontsize: float = 12.0,):from matplotlib.lines import Line2D n_models = len(model_names) n_metrics = len(metric_names) angles = np.linspace(0, 2 * np.pi, n_metrics, endpoint=False) angles_closed = np.concatenate([angles, [angles[0]]])关键初始化:
np.linspace将360度(2π)均匀分配给各个指标angles_closed在末尾添加第一个角度,使得多边形能够闭合雷达图的坐标系统设置至关重要。需要确定网格的最大半径、外环的位置和宽度等。这些参数都是基于数据的最大值动态计算的。
max_val = float(np.max(values)) if values.size else1.0 r_grid_max = 1.10if max_val <= 1.0else _round_half_up(max_val, 2) r_ring_inner = r_grid_max r_ring_width = r_grid_max * 0.155 r_outer = r_ring_inner + r_ring_width r_ring_mid = r_ring_inner + r_ring_width / 2 ax.figure.patch.set_facecolor("white") ax.set_facecolor("white") ax.set_theta_offset(np.pi / 2) ax.set_theta_direction(-1) ax.set_ylim(0, r_outer + r_grid_max * 0.03) ax.set_axisbelow(True)参数说明:
theta_offset=np.pi/2:使0度位于顶部(12点钟方向)theta_direction=-1:设置为顺时针方向(符合常规习惯)外环是雷达图的重要视觉元素,它不仅美观,更重要的是承载了指标名称。使用ax.bar函数绘制扇形块,每个扇形对应一个指标,颜色各不相同。
seg_width = 2 * np.pi / n_metricsfor i, (ang, label) in enumerate(zip(angles, metric_names)): ax.bar( ang - seg_width / 2, r_ring_width, width=seg_width, bottom=r_ring_inner, align="edge", color=ring_colors_local[i % len(ring_colors_local)], edgecolor="white", linewidth=2.0, zorder=1, ) _draw_ring_label(ax, label, ang, radius=r_ring_mid, fontsize=label_fontsize)绘制细节:
seg_width = 2π / n_metrics,确保所有扇形均匀分布ang - seg_width / 2使扇形以角度为中心对称i % len(ring_colors_local),即使指标数量超过颜色数量也能正常工作edgecolor="white"和linewidth=2.0创建明显的分隔线_draw_ring_label函数,标签会自动沿圆环方向排列网格线帮助读者准确读取数值,设置了径向网格线(虚线)和同心圆网格线,并配置了坐标轴刻度。
ax.set_xticks(angles) ax.set_xticklabels([""] * n_metrics) ax.set_rlabel_position(0) yticks = [ _round_half_up(r_grid_max * 0.25, 2), _round_half_up(r_grid_max * 0.50, 2), _round_half_up(r_grid_max * 0.75, 2), _round_half_up(r_grid_max, 2), ] ax.set_yticks(yticks) ax.set_yticklabels([f"{t:g}"for t in yticks], fontsize=10, fontweight="bold", color="black") grid_color = "#7F7F7F" ax.yaxis.grid(True, linestyle="--", linewidth=0.9, color=grid_color, alpha=0.6) ax.xaxis.grid(True, linestyle="--", linewidth=0.8, color=grid_color, alpha=0.5) ax.spines["polar"].set_color("#333333") ax.spines["polar"].set_linewidth(1.8) theta = np.linspace(0, 2 * np.pi, 720) ax.plot(theta, np.full_like(theta, r_outer), color="#333333", linewidth=1.2, alpha=0.9, zorder=2)网格设计要点:
f"{t:g}"自动去除不必要的零和小数点这是雷达图的核心部分,为每个模型绘制一条多边形线,并填充淡色区域。所有模型使用统一的圆形标记,只通过颜色区分。
# 统一圆点 marker(图例与 RF 一致),只变化颜色for i in range(n_models): row = values[i, :] row_closed = np.concatenate([row, [row[0]]]) c = line_colors_local[i % len(line_colors_local)] ax.fill(angles_closed, row_closed, color=c, alpha=0.05, zorder=3) ax.plot( angles_closed, row_closed, color=c, linewidth=1.6, marker="o", markersize=5, markerfacecolor=c, markeredgecolor="white", markeredgewidth=0.9, zorder=4, )绘制策略:
ax.fill绘制填充区域,alpha=0.05使得填充非常淡,不会遮挡其他数据marker="o")markersize=5)markeredgecolor="white")增强对比度zorder参数控制绘制顺序,确保标记在最上层在实际应用中,经常需要尝试不同的配色方案,以适应不同的论文风格或期刊要求。export_multi_palette函数实现了批量生成多套配色方案的功能。
定义了4套不同的配色方案,每套都有其特点和适用场景。
defexport_multi_palette(): palettes = [ ("P1", "Classic (Red/Blue/Orange/Green)", ["#C00000", "#2E75B6", "#E69F00", "#2CA02C", "#9467BD", "#8C564B"]), ("P2","Okabe-Ito", ["#0072B2", "#D55E00", "#009E73", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442", "#000000"], ), ("P3", "Tableau(tab10)", "tab10"), ("P4", "Dark2", "Dark2"), ] ring_colors_local = ["#B7DEE8","#C5D9F1","#F4CCCC","#F9CB9C","#FFE599","#D9EAD3","#D0E0E3","#D9D2E9","#FCE5CD","#FFF2CC","#EAD1DC","#D0CECE", ]配色方案说明:
外环颜色:使用柔和的 pastel 色调,12种颜色循环使用,确保外环美观且不抢夺数据焦点。
model_performance.csv,修改CSV_NAME变量model_performance_radar_P1.jpegmodel_performance_radar_P2.jpegmodel_performance_radar_P3.jpegmodel_performance_radar_P4.jpegmodel_performance_radar_4palettes.jpeg现在绘图代码都不支持免费获取了,20/篇文章,同时欢迎加入小编科研绘图VIP群,198/年,保证每年更新40篇以上的科研绘图相关文章,涵盖机器学习模型(回归和分类)的shap分析、还有各种如皮尔逊分析等相关的图,以及期刊复现图,源代码直接复制或者打开就能绘图。同时进群还赠送微信推文中所有绘图代码以及科研绘图SHAP分析软件和依赖图分析软件,且免费更新使用。后期还会免费赠送一些科研绘图小软件,还有拼图软件(开发中)!!!!