尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

用Python绘制赛后数据雷达图:数据清洗到归一化完整实战

用Python绘制赛后数据雷达图:数据清洗到归一化完整实战 观赛的时候赛后数据面板上的雷达图总能第一时间告诉我们“这场比赛到底谁更全面”。不过很多人只是看一眼颜值并没有真正拆解过雷达图背后的数据口径、标准化方式和绘制逻辑。这篇教程就以 NIP 2-1 WBG 这场系列赛为例从数据维度选取、数据清洗、归一化处理到雷达图绘制完整走一遍数据可视化流程。没有复杂的平台依赖只要本地有 Python 环境就能跑既能帮你读懂赛后雷达图也能让你以后自己动手做任何比赛的数据对比。1. 背景与核心概念1.1 什么是赛后数据雷达图雷达图Radar Chart又叫蜘蛛网图是一种在二维平面上展示多维度数据对比的图表。它将每个维度作为一条从中心向外辐射的坐标轴各维度数据按照数值大小落在对应轴上最后把所有数据点连接成一个闭环多边形。维度越多多边形越接近“蛛网”形状。在英雄联盟赛事中赛后数据雷达图通常用来对比两支战队的综合表现。常见的维度包括击杀、死亡、助攻、经济、推塔、小龙、大龙、分均伤害、视野得分等。由于这些指标的数值量纲和范围差异很大比如击杀一般是个位数分均伤害可能是上千的数值直接绘制会扭曲图形比例因此绘制前必须做标准化处理。通过雷达图读者可以快速判断哪支战队在资源控制上更主动哪条线在输出端承担了更多责任视野端哪一方占优整体运营节奏是偏中期团战还是偏后期单带。1.2 雷达图解决什么问题赛后数据面板传统的表格形式虽然信息完整但不够直观。用户需要逐个数值去对比比如“经济领先多少”“小龙拿了几个”一旦维度增多大脑很难快速形成整体印象。雷达图把多维数据压缩成一张图让读者从“看数据”变成“看形状”。例如一支战队如果在小龙、大龙、推塔三个维度上全部外扩说明它在资源控制端有绝对统治力。而另一支战队可能在经济和击杀维度同样外扩但视野得分明显偏低这也能反映运营细节上的差异。雷达图的价值不在于“画得漂亮”而在于“一眼看出结构差异”。这也是赛后分析、战队报告和战术复盘里高频使用雷达图的原因。1.3 雷达图的局限雷达图也有明显的局限性。它不适合展示时间序列变化也不适合精细对比数值大小。当两个多边形面积接近时数值差异容易被视觉忽略。此外维度之间如果存在高度相关性比如“击杀”和“助攻”天然相关雷达图会放大这种重叠信息给读者造成“虚假的全面性”。因此在赛事分析中雷达图通常作为第一层概览工具精确的数据对比还需要配合柱状图、折线图和原始数值表格。2. 环境准备与版本说明2.1 开发环境本文示例使用 Python 完成核心依赖如下工具/库用途Python 3.9运行环境pandas数据清洗与转换matplotlib雷达图绘制numpy数值计算与归一化处理如果本地没有安装相关库可以执行下面的命令安装pip install pandas matplotlib numpy2.2 示例数据说明本文以 NIP 与 WBG 的系列赛为例但实际比赛打点数据来自赛后官方接口不同来源字段名可能不一致。为了保证教程可复现同时避免数据口径问题本文使用一组模拟数据作为示例数据结构完全参考常规赛后数据面板。大家在真实项目中只需要把数据源换成自己的数据表后续的清洗和绘图逻辑可以原样复用。2.3 项目文件结构match_radar/ ├── data/ │ └── match_stats.csv ├── radar_chart.py └── output/ └── radar.png3. 数据准备与标准化3.1 原始数据结构赛后数据雷达图通常需要整理成二维表格每一行代表一支战队每一列代表一个统计维度。下面是一份模拟数据team,kills,deaths,assists,economy,towers,dragons,barons,damage_per_min,vision NIP,32,27,71,58200,4,3,1,1856,92 WBG,27,32,63,55800,3,1,0,1690,83从业务角度这些维度的含义是kills总击杀数deaths总死亡数assists总助攻数economy总经济towers推塔数dragons小龙击杀数barons大龙击杀数damage_per_min分均伤害vision视野得分。3.2 为什么需要归一化直接把这些原始数值送入雷达图会造成图形失真。原因很简单击杀数是几十经济通常是几万视野得分也可能是几十上百。如果不对数据做缩放经济列会在雷达图上占据绝对主导其余维度的形状全部被压扁。常用的归一化方式有两种极差归一化Min-Max Scaling将数据缩放到 [0, 1] 区间均值归一化将数据按列均值缩放更突出相对差异。赛事雷达图一般使用极差归一化因为它简单直观图形边界固定。但对于需要保留方向性的指标比如死亡数“越低越好”还需要做反向映射。3.3 死亡数为什么需要反向映射在大部分指标里数值越大代表战队表现越好比如击杀、经济、小龙。但死亡数是例外死亡越少说明战队控制风险能力强。如果不处理数据死亡数高的队伍会在图上显示“更大”违背实际解读常识。解决办法是取倒数或者用最大值减当前值deaths_reversed max(deaths_column) - deaths_current这样死亡数从“越大越差”变成“越大越好”。经过反向处理后雷达图上的每个维度都统一为“数值越大代表该维度优势越明显”。4. 完整实战绘制 NIP 与 WBG 赛后数据雷达图4.1 创建项目结构在本地创建以下目录结构match_radar/ ├── data/ │ └── match_stats.csv ├── radar_chart.py └── output/其中data目录存放原始数据output目录存放生成的雷达图。4.2 准备数据文件在data/match_stats.csv中粘贴以下内容team,kills,deaths,assists,economy,towers,dragons,barons,damage_per_min,vision NIP,32,27,71,58200,4,3,1,1856,92 WBG,27,32,63,55800,3,1,0,1690,834.3 编写雷达图绘制脚本以下代码分为数据读取、数据清洗、归一化、绘图四个步骤。请将代码保存为radar_chart.py。# 文件路径match_radar/radar_chart.py import pandas as pd import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Polygon from matplotlib import font_manager # 1. 读取数据 df pd.read_csv(data/match_stats.csv) print(原始数据) print(df) # 2. 数据清洗 # 设置战队名称为索引 df.set_index(team, inplaceTrue) # 对死亡数做反向映射数值越高代表表现越好 df[deaths_reversed] df[deaths].max() - df[deaths] # 选择用于雷达图的维度 features [ kills, deaths_reversed, assists, economy, towers, dragons, barons, damage_per_min, vision, ] plot_df df[features].copy() # 3. 归一化极差归一化 def min_max_normalize(series): return (series - series.min()) / (series.max() - series.min()) normalized_df plot_df.apply(min_max_normalize, axis0) # 把归一化后的数据映射到 [0, 1] 区间方便绘制 # 注意如果某一列最大值等于最小值会出现除零这里做保护 normalized_df normalized_df.fillna(0) print(\n归一化后的数据) print(normalized_df) # 4. 绘制雷达图 # 中文字体配置 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False # 角度计算 categories list(normalized_df.columns) N len(categories) # 计算每个维度的角度并保证首尾闭合 angles np.linspace(0, 2 * np.pi, N, endpointFalse).tolist() angles angles[:1] # 创建画布 fig, ax plt.subplots(figsize(8, 8), subplot_kwdict(polarTrue)) # 绘制两条战队数据 for team_name, row in normalized_df.iterrows(): values row.tolist() values values[:1] # 闭合 ax.plot(angles, values, linewidth2, linestylesolid, labelteam_name) ax.fill(angles, values, alpha0.15) # 设置雷达图刻度 ax.set_xticks(angles[:-1]) ax.set_xticklabels(categories, fontsize10) # 雷达图径向刻度 ax.set_ylim(0, 1) ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0]) ax.set_yticklabels([0.2, 0.4, 0.6, 0.8, 1.0], fontsize8) # 标题与图例 plt.title(NIP vs WBG 系列赛数据分析, size16, pad30) plt.legend(locupper right, bbox_to_anchor(1.2, 1.1)) # 保存图片 plt.tight_layout() plt.savefig(output/radar.png, dpi300, bbox_inchestight) plt.show() print(\n雷达图已保存至 output/radar.png)4.4 代码说明这段代码有几个细节值得展开说明。首先是死亡数的反向映射。df[deaths_reversed] df[deaths].max() - df[deaths]把死亡数的语义反转保证雷达图所有维度的方向一致。假如两支战队死亡数分别是 27 和 32反转后变为 5 和 0死亡较少的战队在该维度上获得更高分。其次是极差归一化。min_max_normalize函数对每一列分别做线性缩放。这样处理后数据范围统一到 [0, 1] 区间经济这种大数值列不会霸占整个图形。需要特别注意的是如果某列最大值等于最小值公式分母为 0会产生 NaN 或 Inf代码用fillna(0)做了保护实际项目建议对这类常量列做删除处理。第三是闭合逻辑。雷达图需要把首尾数据相连因此代码里把列表复制了一次并追加到末尾否则图形会缺一条边。4.5 运行与验证在终端中执行cd match_radar python radar_chart.py运行后观察终端输出应该会依次显示原始数据、归一化后的数据并在弹窗中展示雷达图。同时output目录下会生成radar.png文件。预期输出中归一化后的表格大致如下数值以实际运行为准team kills deaths_reversed assists economy towers dragons barons damage_per_min vision NIP 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 WBG 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0为什么会这样因为只有两支战队极差归一化后每列最大值是 NIP最小值是 WBG所以 NIP 全部为 1WBG 全部为 0。这也是雷达图在双队对比时的一个陷阱——当样本只有 2 个时归一化信息量很低图形只会显示一个大面积多边形和一个小面积多边形。4.6 实际比赛解读的调整建议上面的模拟数据是为方便演示但在真实“NIP 2-1 WBG”系列赛中如果只有两队的汇总数据雷达图的对比意义有限。建议做法是按照小场拆分数据把每一小场的击杀、经济、小龙等数据作为独立样本或者纳入该系列赛的数据均值、对手均值、联赛均值作为参照也可以增加更多历史比赛数据让雷达图的归一化参照系更稳定。如果坚持用两队单一汇总数据绘制可以跳过归一化直接以原始数值绘图并配合“数值标注”让读者看清差异。这种图表更像“对比图”而不是“标准雷达图”。5. 案例扩展引入第三维参照系5.1 添加联赛平均数据为了让雷达图更有信息量可以引入“联赛场均数据”作为参照系。这样雷达图就能同时展现三组数据NIP、WBG、联赛平均。归一化时以三组数据一起计算能减少只有两支战队时“非 1 即 0”的失真问题。在data/match_stats.csv中增加一行team,kills,deaths,assists,economy,towers,dragons,barons,damage_per_min,vision NIP,32,27,71,58200,4,3,1,1856,92 WBG,27,32,63,55800,3,1,0,1690,83 LPL_AVG,29,30,67,57000,3,2,0,1770,88重新运行脚本原来的归一化结果会发生变化每列的 0 和 1 被拉回中间值图形变成三个不同层次的多边形对比更直观。这也是赛后数据雷达图的常见做法——不只看双方差距还要看与联赛均值的偏离程度。5.2 解读雷达图形状假设运行后图形显示NIP 的dragons和barons维度明显外扩WBG 的vision维度与 NIP 接近两队towers维度相差不大联赛均值总体居中。这样的雷达图说明 NIP 这轮系列赛在前中期资源控制上优势明显尤其是小龙和大龙的争夺上表现强势。WBG 虽然整体经济不差但中立资源的失守导致地图压力持续累积最终在关键团战阶段陷入被动。当然这只是根据模拟数据做出的演示性解读真实比赛的细节远比雷达图丰富Neutral Objective 的丢失往往和视野、兵线、阵容曲线强相关需要结合具体比赛录像分析。6. 常见问题与排查思路6.1 雷达图显示为直线或异常多边形问题现象常见原因解决思路雷达图几乎是一条直线数据维度只有 2 个样本归一化后非 0 即 1加入联赛均值或者多小场数据作为参照系图形出现尖角数据没有归一化某些列数值偏大对数据进行 min-max 标准化或 z-score 标准化图形缺一条边忘记将首尾数据闭合检查是否将第一个数据点追加到列表末尾图形方向混乱角度数组不是按照顺时针/逆时针统一排列使用np.linspace(0, 2 * np.pi, N, endpointFalse)生成角度6.2 中文显示乱码matplotlib 默认字体不支持中文时雷达图上的维度名会变成方块。解决方案import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False如果系统没有这些字体可以安装中文字体或者在绘图时指定字体路径from matplotlib import font_manager font_path /path/to/your/font.ttf font_prop font_manager.FontProperties(fnamefont_path)6.3 维度数值全部为 0这通常是因为数据列本身是常量列。比如某系列赛两队都没有拿过大龙barons列全是 0极差归一化后全为 0雷达图上该维度会缩成一个点。这是正常现象但会让图表失去可读性。处理方案是删除该维度或改用联赛均值替代。6.4 如何判断数据口径不同网站或接口的统计数据口径可能不同。LPL 官方赛后数据与第三方数据平台在“视野得分”“承伤占比”等指标上可能有差异。如果拿多份数据源去画雷达图最好先统一字段含义和统计规则否则图表画得再漂亮结论也站不住脚。6.5 雷达图面积不等于综合实力雷达图的多边形面积常被用来判断“谁更全面”但这个指标并不严谨。面积大小受维度顺序影响同一组数据改变维度顺序后多边形面积会改变。因此不要用“面积大实力强”作为解释口径正确的做法是逐个维度分析再综合评估。7. 最佳实践与工程建议7.1 数据源管理比赛数据往往是半结构化数据直接拿 CSV 手动维护容易出错。工程上建议使用数据库或数仓存储每小场比赛明细每个维度字段在入库时统一命名、统一单位保存数据版本避免赛后数据源更新后图表无法回溯对数据变更记录留痕方便复盘和审计。7.2 归一化的选择极差归一化适合雷达图这种固定坐标范围的场景。但如果数据中存在极端离群值比如某场比赛击杀数特别高极差归一化会把其他队伍压到很小的范围。此时可以考虑使用 z-score 标准化或者对数据做截断处理。7.3 图表颜色与可访问性选择战队颜色时要考虑色盲用户的体验。建议使用 Red-Blind 友好的色板同时在线条上加入不同线型或标记点例如 NIP 用实线加圆点、WBG 用虚线加方块这样即使颜色区分度不高也能通过线型区分。7.4 雷达图应配合原始数据表雷达图适合概览不适合精确读取。在实际输出中图下方建议附上一张数据表标明每项数值让读者既能看形状也能查数据。同时可以在维度名后标注“越高越好”或“越低越好”的说明防止误解。7.5 生产环境自动化如果希望每次比赛结束后自动生成雷达图可以封装一个函数def generate_radar(csv_path: str, output_path: str): df pd.read_csv(csv_path) # 清洗与归一化逻辑... # 绘图并保存... logger.info(f雷达图已生成: {output_path})再配合定时任务或 CI/CD 流水线可以在比赛数据更新后自动出图省去手工操作。7.6 数据安全与合规比赛数据本身没有隐私问题但如果在企业内部搭建数据分析平台要注意数据文件不要提交到公共 Git 仓库涉及选手、俱乐部非公开信息时做好脱敏处理对外发布的分析图表应使用统一口径避免造成误解。8. 总结这篇文章围绕 NIP 2-1 WBG 的赛后数据场景完整演示了从原始比赛数据到雷达图的过程。核心内容包括数据维度选择、死亡数反向映射、极差归一化、雷达图绘制和常见问题排查。重点不是画出一张图而是理解雷达图在赛事数据场景下怎么用才不出错。通过本文的示例代码你可以自己做一份战队数据对比图也可以扩展到多战队横向对比。如果把数据源换成 LPL 官方接口的实时打点数据再配合定时任务就能搭起一个简易的赛后数据自动分析工具。接下来可以尝试加入小场次维度的堆叠柱状图或者用时间序列折线图观察经济曲线变化这些都能和雷达图形成一个更完整的赛前赛后分析体系。希望这篇文章能帮你少走一些弯路。如果实际跑代码时有任何报错优先检查数据文件路径和 matplotlib 字体配置这两个是最常见的坑。欢迎收藏备用也欢迎在评论区交流你的雷达图使用经验。
返回列表