ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

R语言雷达图绘制全攻略:从fmsb到plotly的实战指南

R语言雷达图绘制全攻略:从fmsb到plotly的实战指南 1. 雷达图在R语言里的定位不是花架子是多维对比的利器雷达图Radar Chart也叫蜘蛛网图、星状图在R语言的可视化家族里一直处于又爱又恨的位置。爱它的人觉得它在展示多维数据对比时一目了然恨它的人觉得它容易沦为花架子——指标一多就挤成一团蜘蛛网根本读不出信息量。我自己的看法是雷达图不是用来炫技的它的核心价值在于同一维度下的多对象对比。比如你手上有一批样本每个样本测了5个指标你想让人一眼看出哪个样本在哪个指标上占优势或者你做了一堆算法模型想对比它们在准确率、召回率、F1值、训练耗时、内存占用这五个维度的综合表现——这时候雷达图比堆五六个柱状图高效得多。结合最近R语言圈子里的热搜词来看雷达图出现的场景远比想象中广泛。做α多样性分析的人拿它对比不同样本组的多样性指数Shannon、Simpson、Chao1、ACE做单细胞测序的人拿它展示不同细胞亚群的marker基因表达强度做GO富集分析的人拿它对比不同GO条目的富集显著性还有做时间序列的人用SARIMA模型评估不同参数组合的拟合效果。说实话雷达图在生信领域的热度比在纯数据分析领域高得多原因很简单生信结果动辄几十个样本、几十个维度雷达图能帮你在答辩或者文章配图里快速讲清楚一个核心结论。这篇文章就从一个实际干活的角度把R语言画雷达图的方案、代码、坑和场景一次说清楚。不管你是刚入门R的小白还是已经在跑单细胞流程的老手应该都能在里面找到一段能直接抄走的代码。2. 画雷达图前必须想清楚的三个问题2.1 你的数据真的适合雷达图吗这是最重要的一步但90%的人会跳过。雷达图适合的数据有两个特征维度数适中最好在4到10个之间且各维度之间存在此消彼长的对比关系。如果维度少于3个柱状图更清晰如果维度超过10个蜘蛛网会密到根本看不清这时候热图或平行坐标图反而更合适。另外如果各维度之间完全没有对比价值——比如单价和销售量放在一起一个几百块一个几百万——那雷达图表达不了任何东西强制画出来只会让看图的人一头雾水。我见过最典型的翻车现场是有人拿雷达图展示一个模型的好坏五个指标里四个是比率0到1一个是耗时几百秒画出来的图直接压扁了。这种情况的两个解法要么先把数据标准化到同一量纲要么干脆别用雷达图。2.2 你准备用哪个R包决定了后面所有的代码R语言画雷达图的主流方案大概有四条路线我先把它们的定位放在前面fmsb包最经典、最老牌函数只有一个radarchart()上手简单出图风格偏学术严谨但配色和样式需要自己调。ggradar包基于ggplot2语法出图颜值高适合放在论文或PPT里但包更新较慢R版本太新时可能装不上。radarchart包提供了交互功能的底层支持但使用场景相对小众。plotly包严格说是交互式可视化方案雷达图只是它众多图表类型中的一种优点是悬停能看到数值适合做可交互的HTML报告。我个人的建议是论文配图无脑选fmsb因为它稳定、可控、符合学术出版需求做汇报或给客户演示选ggradar或plotly前者好看后者能交互。2.3 数据预处理是雷达图的隐形壁垒很多人在这一步栽跟头。雷达图要求输入数据是行代表指标、列代表对象的格式而且默认第一行是最大值、第二行是最小值作为坐标轴的边界。这意味着你的原始数据通常不能直接丢进绘图函数必须先做两步处理一是把数据调整为矩阵格式宽格式二是为每个指标设定最大值和最小值的范围。如果指标量纲差异大还需要做标准化比如归一化到0到1或按Z-score处理。这些步骤看起来很琐碎但省掉任何一步画出来的图都会偏离预期。3. 实操四种主流的R语言雷达图画法3.1 方案一fmsb包——最稳妥的学术之选先说安装。fmsb在CRAN上有稳定版本直接跑一句代码就能装install.packages(fmsb)装好之后我们用一个实际场景走一遍完整流程——假设你做了α多样性分析手上有5个样本S1到S5每个样本测了4种多样性指数Shannon、Simpson、Chao1、ACE。不过这几个指数的量纲不同Chao1和ACE动辄几十上百Shannon通常只有3到5所以必须先把数值范围理顺。第一步构造数据。为了演示我手动生成一组数据实际使用中你只需要把自己的数据表读进来就行library(fmsb) # 构造原始数据行是样本列是指标 df_raw - data.frame( Shannon c(4.12, 3.87, 4.56, 3.92, 4.01), Simpson c(0.91, 0.85, 0.95, 0.88, 0.90), Chao1 c(86.5, 74.2, 112.3, 68.9, 79.6), ACE c(90.1, 78.5, 120.4, 72.3, 83.2) ) rownames(df_raw) - c(S1, S2, S3, S4, S5)第二步是关键——fmsb需要你显式地给每个指标设定最大值和最小值。我在实际项目中通常不是直接取原始数据的max和min而是稍微放宽一点边界留出内外留白不然折线会贴到坐标轴边缘图很难看# 计算每列的最大值和最小值 max_val - apply(df_raw, 2, max) min_val - apply(df_raw, 2, min) # 构造fmsb要求的输入格式第一行最大值第二行最小值后面是样本数据 df_radar - rbind( max_val, min_val, df_raw )如果你希望各指标之间的对比不受量纲影响就把数据归一化到0到1。这里补充一个简单做法df_norm - as.data.frame(scale(df_raw)) df_norm - as.data.frame(t(apply(df_norm, 1, function(x) (x - min(x)) / (max(x) - min(x)))))第三步画图。fmsb的radarchart()函数参数非常多我挑最常用的一组设置加上中文标签和调色方案# 定义颜色半透明方便叠加观察 colors_border - c(#E63946, #457B9D, #2A9D8F, #E9C46A, #F4A261) colors_fill - c(rgb(230, 57, 70, max 255, alpha 80), rgb(69, 123, 157, max 255, alpha 80), rgb(42, 157, 143, max 255, alpha 80), rgb(233, 196, 106, max 255, alpha 80), rgb(244, 162, 97, max 255, alpha 80)) radarchart(df_radar, axistype 1, # 坐标轴类型1表示常规数值轴 seg 5, # 网格圈数想要更细腻可以加大 pcol colors_border, # 折线颜色 pfcol colors_fill, # 填充颜色 plwd 2, # 折线宽度 plty 1, # 折线类型1为实线 cglcol grey80, # 网格线颜色 cglty 1, # 网格线类型 axislabcol grey30, # 轴标签颜色 caxislabels seq(0, 1, 0.25), # 坐标轴刻度标签 vlcex 1.2, # 顶点标签大小 title α多样性指数对比 )这段代码里值得特别注意的是axistype 1。axistype决定坐标轴的显示方式0表示不显示坐标轴刻度1到5有不同的标签策略。如果是归一化数据通常会配合caxislabels自定义刻度标签如果保留原始量纲坐标轴会自动按最大值显示。第四步加图例。fmsb的radarchart()本身不带图例函数需要手动用legend()叠加legend(x 1.3, y 1.3, legend rownames(df_raw), bty n, pch 20, col colors_border, text.col black, cex 1, pt.cex 2)legend的坐标需要反复调试我的经验是先在默认位置看然后微调x和y。如果你不想花时间直接把这行注释掉用图片编辑软件加图例也行但那样总觉得不够专业。提示fmsb画出来的图默认是正方形的放到论文里如果模板要求固定宽高建议用pdf()或png()设置尺寸时留足边距不然图例容易被切掉。3.2 方案二ggradar包——颜值党的首选ggradar是基于ggplot2的扩展包所以如果你对ggplot语法熟悉上手会非常快。它的安装稍微有点讲究因为包更新慢在R 4.x版本下直接用install.packages(ggradar)不一定能装到最新版我建议从GitHub安装# 如果还没装devtools先装 install.packages(devtools) devtools::install_github(ricardo-bion/ggradar)ggradar的输入数据格式和fmsb不同它要求每行是一个样本每列是一个指标并且第一列是样本分组名称。还是用α多样性的数据来举例但这里我先做一个标准化让所有指标的尺度在同一个可比范围library(dplyr) library(tidyr) df_gg - df_raw # 将每个指标缩放到0到1 df_gg_scaled - as.data.frame(scale(df_gg)) df_gg_scaled - as.data.frame(t(apply(df_gg_scaled, 1, function(x) (x - min(x)) / (max(x) - min(x))))) # 加上样本名列 df_gg_scaled$group - rownames(df_gg_scaled) # 画图 library(ggradar) ggradar(df_gg_scaled, values.radar c(0, 0.5, 1), # 显示在坐标轴上的刻度 group.colours c(#E63946, #457B9D, #2A9D8F, #E9C46A, #F4A261), gridline.min.linetype 1, gridline.mid.linetype 1, gridline.max.linetype 1, label.gridline FALSE, fill TRUE, fill.alpha 0.15, line.width 1.5)这里有个很大的坑ggradar要求所有列的取值必须在坐标轴范围内默认情况下它读取的是0到1但如果你某一列的标准化没有处理好出现了负值函数会直接报错或者画出超出范围的线。所以画图前一定要检查标准化结果。ggradar的好处是图例自动生成、配色自动分配、轴标签按首字母自动排序省了很多手动设置的功夫。坏处是它自动排序候选指标这一点有时候会坑你如果你的指标顺序有业务含义比如时间先后你需要提前在数据框里把列的顺序排好ggradar内部用的是tidyr::pivot_longer之后的最大最小值顺序所以列顺序就决定了图上顶点的顺序。3.3 方案三radarchart包——参数更细但曲线更陡radarchart包的核心函数是chartJSRadar()它最大的特点是底层基于Chart.js可以输出HTML格式的交互图。如果你的报告准备用R Markdown输出成HTML这个方案非常优雅install.packages(radarchart) library(radarchart) # 数据格式每个指标一列每个对象一行 chartJSRadar(scores df_raw, labs colnames(df_raw), maxScale 120, # 必须手动指定通常设为所有指标最大值的上限 showToolTip TRUE, labelSize 15, scaleStepWidth 20) # 轴刻度步长这段代码生成的是一个可交互的网页版雷达图鼠标悬停到顶点上会显示具体的数值非常适合放在项目汇报的HTML文档里。但需要注意chartJSRadar()的中文标签在部分浏览器的默认字体下会显示成方块需要额外引入中文字体。说实话radarchart包在生信圈用得不算多因为大家最终交付的往往是PDF或PNG图片交互式反而没那么必要。但如果你需要把雷达图嵌进Shiny应用或者生成自包含的HTML报告它是最省事的方案。3.4 方案四plotly包——交互式雷达图的万金油plotly是R语言交互可视化的老大哥雷达图只是它的一个图层类型。它的语法是plotly风格的plot_ly()add_trace()本身不复杂library(plotly) # 数据需要先转成plotly要求的格式 # 每个指标一行每个样本一列 df_plotly - df_raw df_plotly$indicator - rownames(df_raw) plot_ly(type scatterpolar, mode linesmarkers, fill toself) %% add_trace( r df_raw[Shannon, ], # 某个样本的值 theta colnames(df_raw), name S1 ) %% add_trace( r df_raw[S2, ], theta colnames(df_raw), name S2 ) %% layout( polar list(radialaxis list(visible TRUE, range c(0, max(df_raw)))), title 雷达图对比 )plotly的优势是交互性拉满缩放、悬停、图例开关全是内置的。劣势也很明显如果样本太多每个样本一条折线图例区会爆炸根本分不清谁是谁。所以plotly雷达图更适合少样本不超过6个对比的场景。4. 雷达图在热门R语言场景里的实战应用4.1 生信方向α多样性分析α多样性是微生物组研究里的高频任务大家习惯用vegan包计算一堆指数然后拿雷达图对比不同分组。这里的关键点是多样性指数之间存在一定的相关性Shannon高通常Simpson也高Chao1和ACE更是高度相关所以雷达图上的折线往往呈现同涨同跌的模式。如果你想在雷达图上突出分组差异建议把各组取均值后再画图同时把误差信息用线条粗细或半透明区间表示。我曾经见过一篇客户的文章就是把对照组和处理组的五个多样性指数均值放在同一张雷达图上然后用fmsb的pfcol填充半透明色两个组的重叠区域一目了然。这种做法比堆五六张柱状图更有说服力。4.2 生信方向单细胞测序与GO富集分析单细胞测序里雷达图最常见的用法是展示细胞亚群的marker基因表达情况。每个细胞亚群算出一个平均表达谱取几个关键marker基因作为维度雷达图上每个亚群的轮廓马上就能反映出这个群高表达什么、低表达什么。比如你用Seurat跑完聚类后挑CD3D、CD14、MS4A1、GNLY、FCGR3A这几个经典marker每个亚群画一个雷达图比单纯看小提琴图直观得多。GO富集分析的结果本质上是一个条目对多个维度的富集打分你完全可以把多个GO条目的-log10(P值)放在一个雷达图里对比不同分组在这些通路上的富集程度。不过这里要提醒一句GO富集结果通常有成百上千个显著条目雷达图只能展示挑出来的那十来个所以图形背后一定要有明确的筛选逻辑比如按P值排序取top条目的代表性通路。4.3 统计建模方向SARIMA模型参数对比SARIMA模型的参数选择通常是个多目标权衡问题AIC赤池信息准则、BIC贝叶斯信息准则、RMSE均方根误差、MAE平均绝对误差各有各的侧重。你网格搜索了十几个参数组合怎么在一张图里告诉别人哪个组合综合最好雷达图就派上用场了。做法是把AIC、BIC、RMSE、MAE四个指标分别做归一化越小越好就把数值取倒数或者用1 - 归一化值然后每个参数组合画一条折线。这样综合表现好的组合其雷达图面积就会更大形状更饱满。4.4 机器学习方向Stacking算法模型评估最近stacking算法r语言实现热度很高大家做完stacking都会拿它和基础模型对比。模型评估的维度通常包括准确率、精确率、召回率、F1和AUC这五个指标天然适合雷达图展示。做法和SARIMA的案例类似但要特别注意一个问题准确率的数值范围通常在0.8到1.0之间直接画雷达图会看不出区别建议以相对最优模型的比值作为绘图数据或者人为把坐标轴范围设窄一点比如从0.8到1.0让差异可视化。5. 常见问题与排查技巧实录5.1 中文标签乱码这个坑几乎人人都踩。fmsb的radarchart()直接画中文标签时如果当前图形设备不支持中文字体就会出现方块字。解决办法有几种# 方法一在画图前指定字体Windows windowsFonts(ARL windowsFont(Arial)) # 或用showtext包最通用 library(showtext) showtext_auto()如果你的R版本比较新推荐用showtext包它对中英文混排的支持非常稳定。我遇到过一种诡异情况用png()输出时正常但用pdf()输出时中文丢失。这种情况多半是pdf字体嵌入问题用pdf(file xxx.pdf, family GB1)指定中文字体族可以解决。5.2 数据维度量纲不一致导致图形被压扁这是雷达图最经典的坑。Shannon是0到5Chao1是几十到几百Simpson是0到1直接画出来的图里低量纲的折线几乎贴在内圈根本看不出变化。我的处理套路分三步先看原始数据分布再做量纲归一化min-max或Z-score最后在图上标注数据已标准化的说明。如果你不想完全丢失量纲信息可以像下面这样自定义坐标轴刻度# 将原始数值映射到0~1后坐标轴显示原始值的对应刻度 caxislabels - c(min_val, (min_val max_val) / 2, max_val) radarchart(df_radar, caxislabels caxislabels, ...)但这么做有个前提所有指标的区间都设成一样的[min_val, max_val]否则坐标轴刻度语义会混乱。不同指标区间不同时不要用这种方式建议直接标准化后按0到1显示。5.3 折线被填色盖住或者图例位置飘忽不定fmsb里如果pfcol设置了填充颜色后面的折线会被前面的半透明填充盖住导致边缘颜色看不清楚。我建议是用浅一点的填充色alpha设低一点并且把plwd加大到2以上让线的权重压过填充的视觉干扰。图例位置的问题更麻烦。legend()的坐标是相对于整个绘图区域的坐标不同尺寸的绘图设备会让图例位置看起来完全不一样。一个稍微省心的方案是画图前先用par(mar c(1, 1, 1, 1))把边距调小然后用legend(bottomright, ...)或者legend(topright, ...)直接指定方位减少手动微调次数。5.4 数据行数多时容易画成一团乱麻如果样本超过8个雷达图的折线会密集到没法看。这时候我通常会选择分面或者分组对比的策略不是把全部样本放在一张图上而是每组挑1到2个代表性样本或者把均值±标准差的范围用灰色区域表示。另一种做法是使用fmsb的radarchart(..., pdensity 30)和pangle 45给多边形加纹理不同组用不同纹理区分虽然代码量会多些但密集图形下的辨识度会明显提升。6. 一些个人经验与工具箱配置建议画了两年多雷达图踩了无数坑之后我目前的固定组合是fmsb负责学术配图plotly负责交互式汇报ggradar偶尔用来做PPT封面图。三者的选择逻辑很简单——看你交付的形式。如果你在写论文需要清晰的静态图并且能放进Word或LaTeXfmsb加showtext是标准答案。如果你在做一个Shiny应用或者给非技术背景的老板做可视化报告plotly的交互悬停功能能帮你省掉大量解释成本。如果你在给公众号或培训班做配图ggradar的高颜值能提升整体的观感。另外强烈建议在项目一开始就把数据预处理封装成一个函数。我通常这样写prepare_radar - function(df, scale TRUE) { if (scale) { df - as.data.frame(scale(df)) df - as.data.frame(t(apply(df, 1, function(x) (x - min(x)) / (max(x) - min(x))))) } max_val - apply(df, 2, max) min_val - apply(df, 2, min) rbind(max_val, min_val, df) }写代码的时候顺手把这个函数存成sources/utils_radar.R以后任何项目需要画雷达图一行source()就能复用。这个习惯帮我省了不少重复劳动。雷达图这个可视化形式本质上是在多维对比和可读性之间反复权衡。它不完美但用对了场景确实能成为数据汇报中的点睛之笔。希望这篇文章能让你少走一些我走过的弯路。
返回列表