ARTICLE DETAIL

资讯详情

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

基于K-Means的篮球运动员聚类分析与实战应用

基于K-Means的篮球运动员聚类分析与实战应用 1. 项目概述与需求拆解看到“基于k-mans对篮球员的聚类分析”这个标题懂行的朋友大概会心一笑k-mans 就是 k-means 的常见手滑写法。这个项目要做的事情其实很单纯把一群篮球运动员的场均技术统计放进算法里让 K-Means 自动把人分成几组。同一组的球员在得分、篮板、助攻这些数字上更接近不同组的球员差异更大。分组结果出来后再回头看看每组的平均数字给每组起一个业务标签比如“核心后卫”“蓝领内线”“3D摇摆人”。这样一来原本只靠球探眼睛看的球员类型判断就变成了一套可复现、可解释的数据分析流程。这个项目适合谁来参考如果你是刚学机器学习想找一个不像鸢尾花那样“太标准”的练手数据如果你是体育数据分析爱好者想搞清楚聚类分析在球员市场里到底怎么用如果你只是拿到一沓球员统计表想知道怎么用最省事的方法把几百号人分成几个组——这条博文都值得看完。我会从需求拆解、数据准备、算法原理、代码实现、问题排查一路走下来最后给出一些完全来自实战的避坑经验。1.1 这个项目到底要回答什么问题篮球场上一个球员值多少钱、该不该续约、放在哪套阵容里好用过去靠球探和教练一双眼睛。但数据时代技术统计已经精确到每分钟完全可以用聚类分析把“感觉”变成“规则”。这个项目要回答的业务问题不是“某球员属于什么位置”而是“联盟里现在存在几种球员类型以及每个球员最像哪一种”。位置标签是人为定义的而聚类是利用得分、篮板、助攻、抢断、盖帽、命中率、出场时间这些连续特征自动发现数据里的规律。对球队管理层来说分类结果能用于交易目标筛选、选秀模板匹配、轮换阵容搭配对普通数据爱好者来说这是理解无监督学习最合适的入门案例。因为篮球统计字段语义清晰几乎人人都懂聚类结果的好坏一眼就能看出来。不像某些电商用户聚类分完还要猜半天“这个簇到底是什么意思”篮球数据分完组看一眼平均值就能解释。1.2 为什么选 K-Means不用层次聚类或 DBSCAN很多新人拿到这个问题会纠结能用聚类分析的算法那么多凭什么用 K-Means我的回答是看数据和场景。手头这份篮球运动员数据通常只有几百行到几千行特征全是连续数值没有明显的环形分布或超密集噪声K-Means 的“基于距离、簇数预先指定”这种玩法完全够用而且训练快、解释简单。算法核心思路优点缺点适合场景K-Means将样本分到离最近中心点的簇迭代更新中心简单快速适合大样本连续特征需要预设 K对离群点敏感球员技术统计聚类层次聚类不断合并或拆分样本生成树状图不需要预设 K能看层次关系样本大时计算慢几十个球员的小样本分析DBSCAN基于密度把连通区域划为一簇不需要预设 K能识别噪声对密度差异敏感参数难调空间位置类异常检测如果你只是把聚类当成探索工具K-Means 是最合适的起点。层次聚类虽然不需要提前指定簇数但篮球数据有几百个球员画出的树状图会非常拥挤切割层次也不直观。DBSCAN 又要调邻域半径和最小样本数参数一变结果差很多对新手不友好。所以这个项目把“宝”押在 K-Means 上是正确的选择。1.3 项目的整体流程与预期产出做聚类分析不是把数据丢给算法就行而是要形成一个完整流程。我这个项目的执行顺序大致是这样确定分析对象与数据口径、清洗数据、特征选择、标准化、确定 K 值、训练 K-Means、解读聚类中心、可视化检查。这套流程同样适用于其他聚类项目可以直接当成模板。最终产出也不只是一张“谁在第几类”的标签表更关键的是每组球员的特征画像、聚类中心的均值表、PCA 降维散点图以及一段能讲给人听的业务总结。比如“第一类是高得分高助攻的核心持球手第二类是篮板盖帽突出的护框型内线”之类的结论而不是干巴巴地甩出几个数字。2. 数据准备与特征选择决定聚类质量的第一步很多人做聚类一上来就急着调 KMeans结果跑出来的图自己都看不懂。其实聚类是个“垃圾进、垃圾出”的典型算法数据准备没做好后面所有努力都白费。篮球运动员聚类也一样选哪些列、清洗哪些行、要不要标准化这三个动作决定了聚类结果的可用性。2.1 数据从哪来原始字段怎么选这类项目的数据来源一般是公开的赛季技术统计网站例如 Basketball-Reference 这类英文体育数据站导出 CSV 后就能用。字段通常非常丰富从场均得分、总篮板、助攻、抢断、盖帽到命中率、罚球命中率、三分命中率、出场时间、效率值 PER甚至还有球员效率、使用率、胜利贡献值等进阶数据。我的建议是第一轮先不要贪多挑八个最核心的字段就够了。我更常用的核心字段组合是场均得分 PTS、场均篮板 TRB、场均助攻 AST、场均抢断 STL、场均盖帽 BLK、投篮命中率 FG%、三分命中率 3P%、场均出场时间 MP。偶尔会加入真实命中率 TS% 和效率值 PER但这两个字段和核心字段相关性很高加不加取决于你想细化到什么程度。这里有个很关键的原则不要直接放入“位置”字段也不要放入“球员名字”。位置本身就是人为标签一旦放进去聚类结果会直接复刻“后卫、前锋、中锋”的分类那算法就没意义了。球员名字更不行K-Means 对纯类别编号做距离计算只会得到一堆没意义的簇。2.2 数据清洗的四个必做动作篮球数据看起来规整实际上坑很多。我拿到原始 CSV 后做的第一件事不是跑聚类而是清洗。下面四个动作我每次都会做。第一设定场次阈值。场均数据如果只打了三五场比赛统计波动很大一个球员可能只打了两场就因伤离场场均 40 分显然不正常。我会把出场次数少于 20 场的球员先过滤掉。当然这个阈值可以根据赛季进度调整如果是赛季中期分析可以降到 10 场但一定要有阈值不能把“样本噪声”送进聚类。第二处理缺失值。命中率和三分命中率经常有缺失因为有些球员几乎不投三分系统可能记录为 0也可能直接留空。留空就有问题KMeans 的欧氏距离计算不允许缺失值。我的做法是如果这个字段缺失比例低于 5%用中位数填充如果缺失比例很高比如超过 30%宁可把字段删掉也不要编造数据。第三剔除极端异常值。场均出场时间不到 3 分钟的饮水机球员技术统计全是小数会对标准化后的数据产生干扰。这类样本要么过滤掉要么单独分析不要让它们拖住聚类中心。第四统一赛季口径。千万不要把不同赛季、不同联赛的球员数据混在一起。我的教训是把 NBA 和欧洲联赛数据放一起聚类结果根本不是球员类型而是联赛差异。即使都在 NBA也建议统一到一个赛季跨赛季的球员状态变化会干扰结果。2.3 为什么必须要做标准化这是所有 K-Means 项目里最容易踩的坑。K-Means 用的是欧氏距离本质上是把每个样本看成一个坐标点然后算坐标点之间的距离。如果特征之间的量纲差异太大数值大的特征会完全主导距离计算。得分字段动辄 20 到 30助攻可能只有 0 到 10盖帽更是 0 到 3如果不做标准化聚类过程基本等于只看得分和篮板其他字段全成了摆设。标准化的方法很多最常用的是 Z-Score 标准化。也就是说把每个特征减掉均值、除以标准差让所有特征变成均值为 0、方差为 1 的分布。标准化之后得分五分的变化和盖帽五分的变化在距离计算里才有可比性聚类结果才真正体现“各种技术的综合相似度”。我见过一个更隐蔽的问题有人先对原始数据做 PCA再用 PCA 降维后的结果聚类却没有做标准化。这会导致 PCA 主成分方向被量纲大的字段带偏前面等于白降维。正确顺序是先标准化再做 PCA 或直接聚类。3. k-means 原理与最佳K值确定算法流程和 K 值选择是聚类分析的核心。以前不少教程只告诉你“这里用 KMeans(n_clusters4) 就完了”完全不解释 K 到底怎么来。实际上K 值选错结果就会从“四类球员”变成“一堆无意义的数字”。所以这一章我会把原理说透再把选 K 的两种主流方法结合起来。3.1 从“k-mans”到 K-Means算法流程拆解K-Means 名字里的 K 是你要分成多少簇Means 是均值。算法的思路可以用一个生活场景来解释你有一堆球鞋想把这些鞋分成 K 堆一开始随便挑 K 只鞋当“代表”然后把每一只鞋分到离它最近的代表那一堆分完之后重新计算每一堆鞋的平均款式再让这个平均值当新的“代表”不断重复“分配—换代表”的过程直到代表不再变化。放到篮球数据里每一次迭代都会做两件事。第一把每个球员分配到距离最近的那个聚类中心第二把每个簇的中心更新为簇内所有球员的平均特征向量。所谓“聚类中心”就是这一组球员的“平均画像”是一个由得分、篮板、助攻等平均值组成的向量。K-Means 的迭代目标是最小化簇内误差平方和 SSE也就是所有球员到各自簇中心的距离平方之和。SSE 越小说明簇内成员越紧凑。但这个目标有个副作用K 越大SSE 通常会越小所以不能单纯靠“SSE 最小”来选 K这就是为什么要用肘部法则。另外要注意K-Means 对初始中心点非常敏感。如果初始点选得不好可能陷入局部最优反复聚类结果都不一样。所以实际使用时要设置 n_init10也就是让算法用不同初始中心跑十次选 SSE 最小的结果。sklearn 的实现里KMeans 默认还会用 k-means 初始化这能尽量让初始中心彼此距离远一点降低随机性带来的问题。3.2 肘部法则和轮廓系数怎么配合选 K 的方法很多我只讲最实用、最能说服人的两种肘部法则和轮廓系数。肘部法则的做法是分别计算 K1、2、3……一直到 10 的聚类 SSE然后把 K 和 SSE 的关系画成折线图。随着 K 增大SSE 会单调下降但下降速度会越来越慢。曲线会出现一个像“手肘”一样的拐点这个拐点对应的 K 就是比较合理的聚类数。拐点之前增加 K 能明显降低簇内误差拐点之后再增加 K 带来的收益很小只会让模型越来越碎。轮廓系数是另一个指标范围在 -1 到 1 之间。它同时衡量每个样本和自己所在簇的紧密程度、以及与最近相邻簇的分离程度。轮廓系数越大说明聚类越合理接近 0 或负数说明簇之间有重叠或样本分错了。实际操作时我会对 K2 到 K10 分别计算平均轮廓系数把“每个 K 的肘部图”和“轮廓系数”放在一起看取两个指标都支持的点作为最终 K。如果肘部图拐点在 K4轮廓系数也在 K4 或 K5 附近出现峰值那 K4 就是首选。如果两个指标冲突比如肘部在 K3轮廓系数最高在 K6我会再结合业务解释性来选三组能不能说清楚六组会不会出现只有一个球员的孤立簇对篮球项目来说一个能讲出故事的 K 比一个数学上最优但无法解释的 K 更重要。3.3 用 SPSS 做 K-Means 的操作路径看到数据分析案例总有人会问不想写代码能不能用 SPSS 做当然可以。SPSS 和 Python 一样能完成这个聚类项目而且点菜单的方式对非程序员特别友好。在 SPSS 里做这个项目的步骤是先把球员统计表导入然后对需要聚类的连续变量做 Z 标准化路径是“分析—描述统计—描述”勾选“将标准化得分另存为变量”。接着点击“分析—分类—K 均值聚类”把标准化后的得分、篮板、助攻、抢断、盖帽、命中率、三分命中率、出场时间选入变量框聚类数填 4。在“迭代”选项卡里把最大迭代次数从默认 10 改到 50防止算法迭代不够就提前停止。最后在“保存”选项卡勾选“聚类成员”运行后球员分组标签就会生成到数据表里。需要注意SPSS 的 K-Means 不会自动帮你算最佳 K所以还是要先用 Python 算肘部图或者在 SPSS 里多跑几次 K3、4、5然后看“ANOVA 表”里的 F 值大小判断哪些变量在簇之间差异大。SPSS 跑出来的聚类中心会直接显示在输出窗口里方便复制到报告里做表格。这套操作对做课程设计、商业报告非常实用。4. 聚类实现与结果解读理论和数据准备都到位之后终于到了跑代码、看结果的环节。我会把 Python 的完整流程拆开来展示然后用一个假设的球员统计场景来解读聚类中心。先说明一点下面的数值是结合常见实践补全的示意不是某一年真实榜单重点在于方法和解读思路。4.1 用 Python 跑通完整聚类流程假设你已经把球员数据存成 CSV 文件包含 GP 场次、PTS、TRB、AST、STL、BLK、FG%、3P%、MP 这些列。核心代码可以这样写import pandas as pd from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt df pd.read_csv(basketball_stats.csv) # 1. 清洗只保留出场次数达到 20 场的球员 df df[df[GP] 20].copy() # 2. 选择聚类特征 feature_cols [PTS, TRB, AST, STL, BLK, FG%, 3P%, MP] data df[feature_cols].dropna() # 3. 标准化 scaler StandardScaler() scaled scaler.fit_transform(data) # 4. 肘部法则看 SSE 下降趋势 sse [] for k in range(1, 11): model KMeans(n_clustersk, n_init10, random_state42) model.fit(scaled) sse.append(model.inertia_) plt.plot(range(1, 11), sse, markero) plt.xlabel(K) plt.ylabel(SSE) plt.title(Elbow Method) plt.show() # 5. 轮廓系数辅助选 K for k in range(2, 11): model KMeans(n_clustersk, n_init10, random_state42) labels model.fit_predict(scaled) score silhouette_score(scaled, labels) print(K , k, silhouette , round(score, 4)) # 6. 选定 K4训练最终模型 final_model KMeans(n_clusters4, n_init10, random_state42) labels final_model.fit_predict(scaled) df[cluster] labels # 7. 查看每类球员的特征平均值 cluster_summary df.groupby(cluster)[feature_cols].mean().round(2) print(cluster_summary)这段代码做的事情很清楚先过滤出场次数不够的球员防止样本波动再选特征、标准化然后用肘部法则和轮廓系数确定 K最后把聚类标签写回原始表方便后续按簇统计平均值。代码里多次出现 random_state42是为了让结果可复现不然每次运行标签编号都会变会干扰分析对比。如果想把聚类结果可视化我会再加一个 PCA 降维。K-Means 在八维空间里聚类人眼看不到所以用 PCA 把八维压缩到二维再画散点图。注意 PCA 必须在标准化后的数据上做from sklearn.decomposition import PCA pca PCA(n_components2) coords pca.fit_transform(scaled) plt.scatter(coords[:, 0], coords[:, 1], clabels, cmapviridis, s50) plt.xlabel(PC1) plt.ylabel(PC2) plt.title(K-Means Clusters on PCA Projection) plt.show()PCA 散点图主要用来做“感觉检查”同簇球员在图上是否聚在一起簇与簇之间是否有清晰边界。如果四个簇在二维图上完全乱成一团那八成是 K 选得不对或特征没选好。4.2 聚类中心长什么样怎么给球员分组画像聚类完成之后最关键的输出是聚类中心均值表。以我手头一份“场均出场 20 分钟以上球员”的示意数据为例假设选定了 K4聚类中心大致可以整理成下面这样聚类PTSTRBASTSTLBLKFG%3P%MP建议标签027.87.26.51.50.851.536.837.5核心持球手116.311.22.10.91.957.224.531.2护框型内线214.63.64.41.20.545.839.630.8空间射手39.85.62.70.90.643.132.024.6角色轮换怎么读这张表不是看绝对值而是看“和别的组比谁明显高、谁明显低”。聚类 0 得分将近 28助攻 6.5出场时间 37.5说明这是球权高度集中、个人进攻能力极强的一批人可以叫“核心持球手”。聚类 1 得分只有 16.3但篮板 11.2、盖帽 1.9、投篮命中率 57.2%三分命中率却很低明显是守在内线干脏活累活的类型叫“护框型内线”很合适。聚类 2 的篮板、盖帽都很低助攻中等但三分命中率 39.6 几乎是四组里最高这是典型的外线投射型球员。聚类 3 各项指标都很平庸出场时间也短属于“角色轮换”。给聚类命名是业务解读中最重要的一步。算法只给你标签 0、1、2、3不会告诉你这些数字代表什么。你得自己看均值结合篮球常识给每个簇起一个可交流的名字。这个命名过程也是验证聚类是否合理的方式如果某个簇的画像根本找不到对应的球员类型那说明数据或者 K 值可能出了问题。4.3 结果可视化PCA 散点图和雷达图除了 PCA 散点图我觉得对篮球聚类最有用的可视化是雷达图。把每个聚类的中心值标准化到同一个尺度后画成雷达图一组一条线可以非常直观地看到“核心持球手”的进攻与组织能力有多突出“护框型内线”的篮板和盖帽有多夸张。绘制雷达图可以用 matplotlib 的极坐标也可以用 pyecharts 这类库。我个人更喜欢自己用 matplotlib 画因为坐标轴可控。做法很简单把每个聚类的标准化均值存成数组角度设为 8 个特征四个簇分别画四条折线再填充一点透明度效果就出来了。雷达图放进数据分析报告里比密密麻麻的数字表格更能说服非技术出身的管理者。这里还要提醒一句可视化不是目的是手段。PCA 散点图只能看聚类是否分离雷达图能看每个簇的特征画像两者配合使用才能形成“算法结果—业务解释—管理决策”的完整链条。如果你只跑完代码、打印一个簇标签就结束那这个项目只能算完成了一半。5. 常见问题与排查技巧实录K-Means 看起来只有几行代码实际跑起来会遇到不少问题。这一章我把新手常见报错和业务层面的坑都整理一下每一件都是我真实踩过或帮别人排查过的。5.1 新手最容易踩的报错速查表问题可能原因解决办法ValueError: could not convert string to float特征列里混入了球员名字或位置等文本字段只选择数值特征列或使用df.select_dtypes(include[float, int])聚类结果每次跑都不一样初始中心随机未固定随机种子设置random_state42并增加n_init10标准化时报 NaN 或 inf原始数据里有空值或除零产生的无穷值先dropna()或填充缺失值再标准化轮廓系数很低甚至为负K 值不合适或数据本身不适合 K-Means减小 K或换 DBSCAN 等密度聚类方法某类只有一两个球员特征极端或 K 选太大检查离群点重新选 K聚类结果完全按出场时间分组没做标准化量纲大的特征主导距离对所有特征做 Z-Score 标准化这些问题里最容易被忽视的是“每次结果不一样”。K-Means 的初始中心是随机的不固定种子的话同一个数据跑两次可能聚类编号和边界都不同。尤其在交付分析报告时这个问题非常致命。我一般会把 random_state 写死并在报告里注明“本结果基于随机种子 42 复现”别人要复核才能一模一样地跑出来。5.2 我踩过的三个坑第一个坑是不做标准化就直接跑聚类。我刚开始分析球员数据时把场均时间、得分、篮板、盖帽一股脑丢进 KMeans出来的四组球员几乎全按“场均时间从高到低”切开。后来才反应过来场均时间数值大、方差也大欧氏距离计算时它一个人就占据了 80% 的权重。标准化之后聚类结果才真正反映球员的技术类型。第二个坑是把“位置”字段当作特征放进去。当时觉得位置能辅助聚类结果 K-Means 直接把数据集切成了后卫组、前锋组、中锋组看起来非常“准确”但完全没发现任何新信息。聚类分析最大的价值是发现数据里原本没被标记的结构如果你把目标属性提前喂给算法结果只是自欺欺人。第三个坑是盲目追求低 SSE把 K 选到 6 甚至 7。K 越大SSE 越低但簇也会越来越碎。当我用 K7 时出现了一个只有一个球星的单一簇这个簇从算法角度没问题但业务上毫无意义。后来我明白聚类项目的评价标准不是数学指标越小越好而是“分出来的组能不能讲出道理”。所以我每次选 K 都会问自己这些组的画像能被球队教练理解吗5.3 聚类结果的业务落地建议聚类分析跑完之后真正的价值在业务层。我跟很多做体育数据的朋友交流后总结出三个落地方向。第一给球探报告提供第一层筛选。如果一个新秀的标准化特征和“核心持球手”聚类中心距离很近那他在进入联盟后大概率具备同类型球员的底子。但这只是“相似性”判断不能替代伤病风险和性格评估只能作为辅助信息。第二用于阵容搭配分析。球队需要为持球核心配射手、配护框内线聚类结果能帮助管理层快速筛选自由市场里类型匹配的球员比一个个看集锦高效得多。比如你已经有一个核心控卫就可以优先关注“空间射手”这一簇里的球员。第三用于球员类型稳定性跟踪。如果同一个球员在赛季前半段和后半段分别做聚类发现他的标签从“空间射手”变成了“核心持球手”说明他在球队战术体系里的角色发生了变化。这个变化可以用来评估培养效果也可以用来判断球员是否适合当前体系。6. 写在最后把这次项目经验变成你的分析工具我在实际项目里反复用过很多次 K-Means 做球员聚类每次感受都不同。最开始觉得只要会调包就行后来才发现真正花时间的不是算法本身而是数据清洗、特征选择和簇数确定。好在这套流程本身是通用的你今天把篮球数据跑通明天换一份商家用户数据后天换一组设备运行参数逻辑几乎完全一样先清洗再标准化再定 K再聚类最后回到业务里去解释。最后我还想分享一个小技巧聚类的簇中心千万别做完就扔。把它存成一张小表以后每来一个新球员就可以算一下他的特征向量离哪个簇中心最近直接给他打一个“类型候选标签”。这一步本质上就是最简单的一种分类预测不用重新训练模型只要维护好聚类中心表就行。随着你手里的赛季数据越来越多还可以定期重跑聚类看球员类型的分布有没有发生变化。这样一次简单的聚类分析项目就能变成一个长期可用的球员类型分析工具。
返回列表