ARTICLE DETAIL

资讯详情

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

R语言雷达图绘制实战:基于ggradar的多维数据对比分析

R语言雷达图绘制实战:基于ggradar的多维数据对比分析 雷达图不是柱状图的替代品而是观察“结构”的放大镜先说一个我常被问到的问题既然有柱状图、条形图为什么还要花心思去画雷达图直接对比数值高低不就好了吗雷达图真正的价值在于把一名球员、一支球队或者任何研究对象的多项指标放在同一个封闭多边形里对比。它看的不是“谁第一谁第二”而是“谁更全面”“谁的风格更偏科”“谁的强项和弱项反差最大”。比如一名球员场均25分5篮板5助攻和另一名球员场均25分10篮板2助攻放在柱状图里只能一列一列比放在雷达图里一个五边形像一个锋位摇摆人另一个则完全是内线蓝领的形状一眼就能看出打法差异。这篇文章就以NBA季后赛球员数据为例用R语言里一个专门画雷达图的包——ggradar把从数据整理到出图的完整流程走一遍。无论你是刚开始学R的数据分析新手还是想找现成绘图方案做球员评估、产品能力对标、用户画像分析的老手这篇都能给你一套可以直接“抄作业”的方案。1. 内容整体设计与思路拆解1.1 雷达图适合处理什么问题不适合处理什么问题雷达图本质上是一个极坐标下的折线图。普通折线图把横轴当成连续的时间或类别雷达图则是把横轴首尾相接围成一个闭合的环每个指标占一条从圆心出发的射线。这种结构的优势非常明显当指标维度较多6到10个时雷达图可以把所有变量汇总到一张图里让人快速把握整体的多维特征。比如篮球数据分析里得分、篮板、助攻、抢断、盖帽、失误、命中率这七个维度要同时比较好几名球员柱状图要拆成七张小图才能看全雷达图一张图就能摆下。但雷达图也有它的局限性这点我必须放在最前面说清楚。第一它不适合做精确比较因为人的视觉对面积和角度的敏感度远不如对长度和位置读数很困难第二指标之间的刻度差异如果太大会被强行压缩到同一个量纲下容易失真第三指标顺序如果调整多边形的形状会发生肉眼可见的变化所以雷达图不适合用来做严格的统计分析它更适合做探索性的视觉展示和方案汇报。也就是说雷达图的定位是“把复杂结构可视化”而不是“精确量化”。理解了这一点后面处理数据、解释结果的时候才不会犯方向性错误。1.2 为什么选择ggradar而不是fmsb或ggplot2手动实现R语言里画雷达图的方案不少。老牌的是fmsb包它的做法是先构造一个特殊的data.frame在上方留出最大值和最小值的两行然后一行代码画出来代码非常简短。ggplot2也有一种土办法先计算极坐标下的路径再手动拼接闭合多边形但坐标变换和标签对齐的代码量相当大而且效果常常达不到预期。ggradar的优势在于它是真正意义上的“ggplot2风格”封装。它把背景网格、轴线、标签、填充透明度全部封装在了一个函数里默认输出就带圆形的等高网格线、清晰的组名标签并且支持直接叠加ggplot2的主题、配色、facet分面。fmsb画出来的图更古老网格是直线放射状的视觉现代感差一些ggradar默认的效果更干净适合直接放进报告里。另外一点非常实际ggradar接收的数据格式和tidyverse系列非常契合。只需要一个data.frame第一列是分组名后面都是数值列传进去就能出图不需要像fmsb那样单独构造最大值和最小值行。这个设计对日常数据分析流来说要顺手得多。不过ggradar也有一个很出名的大坑它对ggplot2的版本兼容非常脆弱早年间在ggplot2 3.x环境里跑大概率会报could not find function coord_radar这样的错误。后面我会专门讲解决方案这里先记住一句话可以选ggradar但要先解决版本兼容和字体两个问题。1.3 为什么要用NBA季后赛数据作为演示案例用NBA季后赛数据来做演示案例是我刻意做的选择。原因有几个第一篮球数据的维度非常丰富得分、篮板、助攻、抢断、盖帽这几项是现成的多维指标不需要造假也不需要编造解释维度省去了说服读者的成本。第二NBA球员的现实分布天然具备“不同类型的球员结构差异大”的特点。内线球员篮板盖帽高外线核心得分助攻高你不需要设计什么实验直接用真实感觉就能验证雷达图是否准确反映出了差异。第三季后赛数据本身比常规赛更有辨识度。常规赛还能靠大量场次平均平滑掉一些特征季后赛系列赛紧张强度大球权集中在几名核心手里雷达图往往能更鲜明地体现“谁是攻防一体核心”“谁是纯得分手”“谁是组织大脑”。为了演示方便我将构建一个模拟2023年季后赛核心球员在得分、篮板、助攻、抢断、盖帽按场均计表现的小型数据集主要目的不是去验证某位球员的排名而是完整演示从原始数据到最终雷达图的处理链路。2. 工具准备与数据构造2.1 安装与加载ggradar及相关包安装包这一步没什么好说的直接用install.packages()就可以。需要注意的是一些底层包要提前确认装好比如ggplot2、dplyr、tibble以及scales因为ggradar的内部实现会引用scales里的坐标变换函数。install.packages(ggplot2) install.packages(dplyr) install.packages(tidyr) install.packages(tibble) install.packages(scales) # ggradar 不在 CRAN 上需要使用 remotes 从 GitHub 安装 if (!requireNamespace(remotes, quietly TRUE)) { install.packages(remotes) } remotes::install_github(ricardo-bion/ggradar)之所以用GitHub而不是CRAN是因为ggradar一直只以开发版的形式存在。安装完成后我建议你马上做一次加载测试确认当前环境里的ggplot2版本兼容。library(ggplot2) library(dplyr) library(tidyr) library(tibble) library(scales) library(ggradar) # 加载测试如果提示找不到 coord_radar说明需要手动补函数见第3.3节 print(ggradar包加载完成)2.2 构造NBA季后赛模拟数据接下来构造示例数据。这里我用的是一个小规模、可复现的数据框。假设我们拿到了四位球员在2023年季后赛的场均数据A某全队核心锋线、B某内线支柱、C某外线组织者、D某防守型拼图。为了贴合真实场景我按常规的统计口径设置了得分、篮板、助攻、抢断、盖帽五个维度。nba_playoff_data - data.frame( 球员 c(A某, B某, C某, D某), 得分 c(26.8, 18.2, 20.5, 12.4), 篮板 c(9.1, 12.6, 4.2, 7.8), 助攻 c(5.7, 2.8, 8.9, 2.1), 抢断 c(1.4, 0.8, 1.6, 1.9), 盖帽 c(0.9, 2.3, 0.3, 1.5) ) print(nba_playoff_data)这组数据大概描述的是这样四类球员A某是攻守兼备的锋卫各项数据相对均衡B某是纯内线得分和篮板突出但组织能力弱C某是外线发动机助攻抢断数据好看但护框能力几乎没有D某是防守工具人抢断盖帽不错但进攻存在感低。用一个虚构的例子而不是直接从某个网站爬真实数据有以下考虑一是方便大家无阻断复现不需要处理网络请求和解析的问题二是雷达图的绘制重点不依赖数据的真实性而在于怎么整理、怎么映射、怎么调参。如果你想换成真实球员数据直接替换这个data.frame即可后面的绘图代码一行都不用改。2.3 数据清洗和标准化ggradar的数据格式要求这是整个演示步骤中最容易被跳过、也最容易出问题的一环。ggradar对数据的格式是有要求的而且和很多R包不太一样。第一个要求数据的第一列必须是分组变量而且这一列必须是字符串或因子类型。后面的所有列都必须是可以转换成数值型的列。我这里第一列是“球员”满足条件。第二个要求所有指标列的数据不能有负值。雷达图是极坐标从圆心向外是正方向如果出现负值不仅绘图时会拉伸异常而且坐标轴的范围也会变得难以控制。这一点在后续使用真实数据时要特别注意比如有些效率指标是偏差值可能是负数就需要先做平移或归一化处理。第三个要求各列的数值量纲可以不同但最好先做归一化处理。ggradar会默认帮我们设置轴的最大值和最小值但如果你不手动指定它会把所有指标统一到0到某一最大值的区间这样就无法体现“哪个指标整体水平高”。更常见的问题是指标单位差异巨大比如助攻平均数可能只有七八抢断才一点几放在同一个雷达图里小的指标会全部挤在圆心附近根本看不出来差异。所以这里需要针对每个指标列做归一化。常用的办法是把每个值除以该维度的最大值让它变换到0到1之间。因为季后赛数据量级差异很大归一化之后才能公平地说“这个球员是不是比其他人更会抢断”这种结构性问题。nba_playoff_scaled - nba_playoff_data %% mutate(across(得分:盖帽, ~ . / max(.))) print(nba_playoff_scaled)这样处理后每个球员在每个维度上的取值最大就是1最小可能接近0。比如B某的盖帽如果所有球员里最高那它的归一化值就是1其他球员的盖帽按比例变小。这个处理不是为了精确比较绝对值而是为了把每个维度的“相对水平”呈现出来让雷达图真正表达球员之间的结构特征对比。2.4 选择哪些指标指标数量多少合适这里我要多说一句因为雷达图是否“一眼看得清”很大程度取决于指标数量。经验法则是5到8个维度最佳。少于4个雷达图会变得非常细长看不出“仓体”的感觉多于10个标签和轴线会密集成一团看起来反而不清晰。我选的五个维度得分、篮板、助攻、抢断、盖帽恰好覆盖了NBA球员攻防两端的核心贡献结构清晰、标签不会重叠。如果你要扩展到三分命中率、罚球命中率、正负值等进阶指标也可以但要考虑对齐刻度和标签字体大小。后面我在常见问题部分会专门讲指标太多怎么处理。3. 核心绘制环节:ggradar的完整实操3.1 从一行代码开始先看默认效果数据准备完毕现在真正开始绘制。ggradar最爽的地方就体现在这里:一行代码就能出图。ggradar(nba_playoff_scaled)这个默认图会直接输出到RStudio右下角的Plots窗口。第一次跑的读者如果看到的是一个有多彩闭合多边形、带灰色圆形背景网格、每个方向轴位置有对应指标名称的标准雷达图就说明你的环境基本没问题可以进入到调参环节了。不过实际使用时我几乎不会直接用默认参数。两个原因第一默认输出的配色是ggplot2默认的粉红、青蓝、绿、紫在PPT或者报告里违和感太强第二默认的图例位置、透明度、网格线条颜色都比较偏学术风缺少一些锐度。不过这正说明ggradar的封装思路是对的——先保证画出来的是完整正确的图再让用户往细节上调。3.2 参数逐项拆解线条、填充、网格、坐标轴、图例现在把ggradar()函数里我常用的参数列出来一个一个讲清楚。ggradar( plot.data nba_playoff_scaled, font.radar sans, values.radar c(0, 0.5, 1), axis.labels colnames(nba_playoff_scaled)[-1], grid.min 0, grid.mid 0.5, grid.max 1, group.colours c(#E64B35, #4DBBD5, #00A087, #F39B7F), line.width 1, fill TRUE, fill.alpha 0.15, background.circle.colour #E6E6E6, gridline.colour #D9D9D9, legend.position bottom, legend.text.size 12, plot.title 2023年NBA季后赛核心球员数据对比 )逐个说明plot.data传入的数据框。它必须满足前面说的格式要求第一列是分组后面是指标列。font.radar标签字体一般用sans就够了中文环境下配合后面讲的showtext方案改成对应中文字体名更稳妥。values.radar三个字符串表示网格从内到外标注的刻度值。因为我做了0到1的归一化所以这里写0、0.5、1。axis.labels作为轴标签的向量。注意这里要取原数据框去掉第一列之后的列名不能把球员列当指标名。grid.min / grid.mid / grid.max网格的最小值、中间值、最大值和values.radar对应。因为是归一化数据统一设成0、0.5、1。group.colours每一个分组的折线颜色。四名球员就传四个颜色向量。这里我推荐用一款对色盲友好并且对比明显的配色比如RColorBrewer里的Set2或杂志风格的低饱和颜色。line.width折线粗细。默认1可以如果图在PPT里要放大会显得蹭得慌可以调到1.2或1.5。fill是否填充多边形内部区域。如果不填充只有外框看起来会更简洁适合线很多的情况如果填充视觉层次感更强适合只有三名球员以内的情况。我这里选择TRUE填充色透明调低。fill.alpha填充色的透明度。0.15是保守值多边形的底层互相重叠时不容易完全糊掉。background.circle.colour和gridline.colour最外层圆圈的边框色和内部网格线条颜色。我习惯用浅灰色让前景的彩色多边形更跳。legend.position图例位置推荐bottom放在底部避免顶部标签被遮住。plot.title加个主标题配合labs()还可以继续添加副标题和题注后面会提到。参数看起来多但大多数只用调一次真正每次都会改的其实就是group.colours、fill和legend.position。3.3 必踩的坑coord_radar 未定义与版本兼容问题现在到了最重要的一节。很多人在安装好ggradar之后第一次运行就会遇到下面这个报错Error in coord_radar() : could not find function coord_radar这个报错的原因非常明确ggradar在内部调用ggplot2时使用的是它自己定义的一个坐标变换函数coord_radar()。但ggplot2在3.0版本之后调整了坐标系统的扩展机制ggradar的早期版本没有及时跟进所以坐标函数没有注册到ggplot2的命名空间里。此外ggplot2新版里不再允许在coord_radar里调用已废弃的coord_radar内置写法这就导致版本越新越容易出错。解决方案有两种。第一种在加载ggradar包之后手动定义一个和ggradar兼容的coord_radar函数。这个函数负责创建一个极坐标类坐标系统内部会调用ggplot2的ggproto对象。coord_radar - function(theta x, start 0, direction 1, clip off) { theta - match.arg(theta, c(x, y)) r - if (theta x) y else x ggproto( CordRadar, CoordPolar, theta theta, r r, start start, direction sign(direction), clip clip, is_linear function(coord) TRUE ) }这是社区里流传最广的兼容方案。定义完之后再运行ggradar()就不会报“找不到coord_radar”了。第二种方案是把ggplot2降到2.x旧版本例如安装ggplot2 2.2.1但这种方法实操起来很不方便还会连带影响其他包所以我不推荐。唯一可能用到旧版本的情况是公司内部环境被锁死、不能安装GitHub包的时候。这里我要强调一个操作顺序先加载ggplot2再定义coord_radar最后再调用ggradar。因为ggradar内部是直接调用coord_radar这个函数的它不会管你是否加载了ggplot2。很多人报错是加载顺序放反了。还有一个隐藏问题如果你安装了多个R版本的ggplot2或者你从不同的library路径加载了旧版ggradar也可能会因为命名空间冲突出现类似报错。最好的做法是先用sessionInfo()确认当前加载的ggplot2和ggradar版本。sessionInfo()看到ggplot2 3.x版本基本就是需要手动定义coord_radar的环境。3.4 利用ggplot2扩展添加主题、调整字体、完善排版解决完兼容性后ggradar生成的对象本质上还是一个ggplot对象。这就意味着所有ggplot2的主题函数、labs函数、facet分面、ggsave导出都可以继续作用于它。这非常方便。p1 - ggradar( plot.data nba_playoff_scaled, font.radar sans, values.radar c(0, 0.5, 1), axis.labels colnames(nba_playoff_scaled)[-1], grid.min 0, grid.mid 0.5, grid.max 1, group.colours c(#E64B35, #4DBBD5, #00A087, #F39B7F), line.width 1.2, fill TRUE, fill.alpha 0.2, background.circle.colour #E6E6E6, gridline.colour #D9D9D9, legend.position bottom, legend.text.size 13, plot.title 2023年NBA季后赛核心球员数据对比 ) theme( plot.title element_text(size 16, face bold, hjust 0.5), plot.subtitle element_text(size 11, hjust 0.5, color #666666), plot.caption element_text(size 9, hjust 0.5, color #999999), panel.grid element_blank(), panel.background element_rect(fill white, color NA), legend.background element_rect(fill white, color NA), legend.key element_rect(fill white, color NA) ) labs( subtitle 指标已按各维度最大值归一化处理, caption 数据来源示例模拟数据 ) print(p1)这里需要注意多边形的坐标层是加了radar坐标变换的所以在里面改panel的背景、网格线颜色不一定能看到效果。更常见的情况是你只想调整图例、标题和整体画布那就直接在theme里改这些部分就行。3.5 导出高清图片ggsave 与画布尺寸绘图的最后一步是导出。雷达图和普通柱状图比对画布尺寸的敏感程度更高如果画布太窄圆的纵宽比例会被压缩整个图看起来像一个鸡蛋如果画布太宽标签又会偏离太远。所以导出时要特别注意width和height的比例。ggsave( filename nba_playoff_radar.png, plot p1, width 8, height 8, units in, dpi 300 )我一般选择8乘8英寸正方形比例。dpi设成300这样放在PPT或者打印出来都足够清晰。如果需要透明背景在ggsave里加bg transparent即可。4. 让雷达图更进一步的细节打磨与扩展4.1 中文字体问题:方块字怎么破这个问题太普遍了必须单独拿出来讲。默认情况下RStudio在Windows和macOS上画图时ggplot2的默认字体是不带中文的你在轴标签或标题中输入中文最后导出的图片上全是方框“口口口”。解决思路有两个。第一个是设置系统支持的中文字体名Windows上可以试SimHei或Microsoft YaHeimacOS上是PingFang SC或STHeiti。theme(text element_text(family Microsoft YaHei))这种方法简单直接但跨平台可复现性差换一台电脑效果就变了。第二个思路是用showtext包它能把字体以数据驱动的形式嵌入到图片里而且通过互联网字体库可以离线缓存字体导出后换机器也不会变方块。install.packages(showtext) library(showtext) font_add(heiti, simhei.ttf) # 本地字体文件路径可换成自己的 showtext_auto()showtext用起来也很简单在绘图前启用showtext_auto()然后在theme里设置family heiti就行。导出的图片会自带字体信息不会再出现跨平台乱码。4.2 常规做法之外:用facet按球员逐个子图展示ggradar还有一个很妙的地方就是它和ggplot2的facet_grid可以直接联动。如果你有四名球员数据混在一起默认画在一张图上彩色线条可能会相互交叉虽然有填充色半透明兜着但密集时还是难以分辨。这时可以按球员分面每名球员单独输出一张子图统一坐标轴方便对比结构差异。nba_playoff_long - nba_playoff_scaled %% pivot_longer(cols 得分:盖帽, names_to 指标, values_to 数值) p2 - ggradar(nba_playoff_scaled) facet_wrap(~球员) theme(strip.text element_text(size 12, face bold))不过这里有个细节需要注意facet_wrap的“球员”变量需要存在于ggradar传入的原始数据第一列中。由于ggradar是直接绘图不会返回一个带facet分组参数的图层对象所以需要先haver在大数据框里保留这个列。实际操作中上面的写法是可以正常工作的每个分面下圆环都完整显示但如果你发现子图里的标签重叠或者圆被裁切很可能就是画布不够大需要调整ggsave的宽高比。4.3 进阶扩展多赛季趋势叠加与多组对比除了单赛季球员对比雷达图还能用来做同一名球员的多个时期对比。比如你想看一名球员从季后赛首轮到总决赛的技术特点变化可以把每轮系列赛作为一行五维指标做归一化后丢进同一张雷达图。折线增多时建议把fill参数设为FALSE只保留外框线这样可以避免层叠填充色盖住细节。还可以换一个思路把不同球队的团队数据聚合成一条线。因为球队的进攻风格和防守特点差异很大一幅雷达图上放两三支不同风格的球队能很直观地解释数据所体现的“打法”。我试过把联盟真实球队的助攻率、失误率、防守篮板率、抢断率、盖帽率同时画在雷达图里。这种“效率比”类的数据天然适合雷达图展示因为每个维度是0到1之间的比例天然满足非负条件。不过效率比类数据通常量级都集中在0.5左右不做拉伸归一化也能看出差异。这些都说明雷达图的应用场景远不止篮球领域它可以迁移到任何多维度对比分析的工作中。5. 常见问题与排查技巧实录5.1 报错“could not find function coord_radar”这是最经典的问题前面已经提到完整解法。这个方法几乎适配所有ggplot2 3.x环境。如果你按上面的代码定义了coord_radar仍然报错就要检查是否在调用ggradar之前有别的包把coord_radar覆盖了。比如某些扩展包会“屏蔽”掉你自己定义的函数此时可以运行search()查看附加包的顺序把基础环境中的函数放在后面加载问题大概率解决。5.2 图形是扁的或者椭圆不是正圆雷达图理论上在极坐标下是正圆但如果你设置的面板尺寸不是正方形画布就会拉伸成椭圆。比如在RStudio的Plots窗格里用默认大小图形常常是扁的。解决办法很简单手动拉窗格或用ggsave固定方形画布width和height保持相等如8比8、10比10。此外如果标签文字比较大也会影响整体视觉比例可以在theme里把axis.text调小一点。5.3 中文标签变成方框如前面所说用showtext加载系统字体即可。还有一种更“懒”的方式在导出前把所有中文标签临时翻译成英文导出后再在PPT里覆盖中文但这个方法不够优雅还是建议老老实实配置字体。5.4 雷达图的数值显示不准确出图之后如果发现某条折线完全贴在外圈或全部挤在内圈大概率是归一化方式不对。比如“助攻”维度的最大值可能只是8.9而“得分”最大到26.8如果不归一化助攻线就会贴边。理想的归一化是除以每个维度的最大值或使用min-max标准化。若某些指标的分布特别集中比如抢断全部落在1.4到1.9之间归一化后差异会被压缩这时可以改用每个维度先减最小值再除以极差扩大可区分度但要注意这样会丢失绝对水平的差异两种变换各有利弊。为了更直观我把几个常见问题整理成一个速查表现象可能原因排查与解决报错could not find function “coord_radar”ggplot2 3.x版本与旧版ggradar不兼容手动定义coord_radar函数并确保ggplot2先加载中文标签显示为方框系统默认字体不支持中文使用showtext包或显式设置中文字体图形呈椭圆画布宽高比例不一致ggsave使用正方形画布或调整设备尺寸宽度等于高度某条折线完全贴向外圈或内圈指标未归一化或部分值均为0对每个维度做min-max归一化图例和标签重叠画布太小或字太大调小legend.text.size或加大画布尺寸多边形颜色区分度低配色太相近使用高对比配色如Set2或杂志风低饱和色彩5.5 实际操作中我的习惯与经验最后分享一些我个人的实操习惯不一定写在官方文档里但每次画雷达图都用得上的经验。第一先画出来再调参数。不要一上来就纠结颜色、字体、透明度先用默认图确认数据是否正常、结构是否清晰。数据有问题时再漂亮的图也是废图。第二会看“形状”比会调参数重要。雷达图的价值在于快速识别“偏科型”和“全能型”选手。如果多边形的顶点靠近某一个方向特别尖说明这个维度显著强于其他如果整个多边形相对圆润说明各项指标比较均衡。实际汇报的时候我会把这种观察直接讲给听众比列数据更清晰。第三控制信息的密度。同一张雷达图里放的指标数量不建议超过8个如果超过优先考虑因子分析或聚类后的综合维度或者拆分画成两张图。雷达图最怕什么都想放、一放就什么都看不清。第四合理使用透明度。如果同一张图里多个主体对比透明度设置特别重要。太低看不清填充色太高就会互相遮挡。我自己的经验值是0.1到0.25之间看色块总面积再调整。第五归一化时记得备份原值。画图之后如果需要标注真实数值建议在保留的原始列基础上做标准化不要直接覆盖原列否则后面标注和核查数据时还得重新构造。写在最后关于雷达图我能告诉你的数据对比这件事方法太多雷达图只是工具箱里的一件趁手工具。它解决的不是“多精确”的问题而是“一眼看出结构差异”的问题。你完全可以用柱状图、散点图替代它但当你面对NBA季后赛这种多球员、多指标、强对比的数据时雷达图仍然是让结论在汇报现场变得清晰的最快方式。画好一张雷达图技术难点其实就三处数据要整理成符合包要求的结构预处理一定要归一化还有把版本兼容的问题提前预防好。这三步走通了后面就是颜色、字体、导出这些锦上添花的细节了。用ggradar这一套流程我做过球员报告、做过产品能力对标也做过用户画像分析换汤不换药。别把眼光只盯在篮球数据上把这个流程迁移到你手头的业务数据里去用雷达图找出最突出的结构特征它远比一列列数字要直观得多。
返回列表