这次我们用 NBA 球员赛季表现聚类来探讨下 K-Means 算法,K-Means 是一个清晰明白的无监督学习方法,和 KNN 有很多相似点,例如都有超参数 K,前者是 K 个类别,后者是 K 个邻居。

聚类算法是不需要标签的,直接从数据的内在性之中学习最优的分类结果,或者说确定离散标签类型。K-Means 聚类算法是其中最简单、最容易理解的。

简单即高效。我们的目标是学习一个东西,然后把它的思想应用到我们想要探索的场景,以加深对算法的理解。

最优的聚类结果需要满足两个假设:

  1. “簇中心点”(cluster center)是属于该簇的所有的数据点坐标的算术平均值

  2. 一个簇的每个点到该簇中心点的距离,比到其他簇中心点的距离短

于是 K-Means 聚类的工作流为

  1. 随机猜测一些簇中心点

  2. 将样本分配至离其最近的簇中心点上去

  3. 将簇中心点设置为所有点坐标的平均值

  4. 重复 2 和 3 直至收敛

我们会编码实现 K-Means 算法,并用于 NBA 控卫 2020-21 常规赛季的表现分析中。

我们知道过去刚刚结束的这个赛季得分王是萌神库里,我爱他。我们这里只关注控卫,一是因为控卫线上星光熠熠,二是数据分析要细分,这本身是一个原则。

我们开始吧!

01

学习原理方法

找到数据

我们可以从https://www.basketball-reference.com找到我们想要的数据,那就是NBA球员在过去一个赛季的统计数据。数据获取流程如下

看看数据

# 三剑客来一遍
import pandas as pd
import numpy as np
import matplotlib.pyplot as pltnba = pd.read_csv("nba_2020.csv")
nba.head()

我们发现姓名列有个\,后面跟着不知道是什么的简称,可以处理掉。另外字段名全是简写,我一个球迷有很多都看不懂,尴尬。整理了下字段含义,我们看一遍,这样就大概了解了这个数据集有什么了。

  • Age:年龄(别告诉我你知不知道)

  • TM(team):球队

  • Lg(league):联盟

  • Pos(position):位置

  • PG(Point Guard):组织后卫

  • GS(Games Started):首发出场次数

  • MP(Minutes Played Per Game):场均上场时间

  • FG(Field Goals Per Game):场均命中数

  • FGA(Field Goal Attempts Per Game):场均投篮数

  • FG%(Field Goal Percentage):命中率

  • 3P(3-Point Field Goals Per Game):三分球命中率

  • 3PA (3-Point Field Goal Attempts Per Game):场均三分球投篮数

  • 3P% (3-Point Field Goal Percentage):三分球命中率

  • 2P (2-Point Field Goals Per Game):两分球命中数

  • 2PA (2-Point Field Goal Attempts Per Game):场均两分投篮数

  • 2P% (2-Point Field Goal Percentage):两分球命中率

  • eFG% (Effective Field Goal Percentage):有效命中率

  • FT (Free Throws Per Game):场均罚球命中数

  • FTA (Free Throw Attempts Per Game):场均罚球数

  • FT% (Free Throw Percentage):罚球命中率

  • ORB (Offensive Rebounds Per Game):场均进攻篮板数

  • DRB (Defensive Rebounds Per Game):场均防守篮板数

  • TRB (Total Rebounds Per Game):总篮板数

  • AST (Assists Per Game):场均助攻

  • STL (Steals Per Game):场均抢断

  • BLK (Blocks Per Game):场均盖帽

  • TOV (Turnovers Per Game):场均失误

  • PF (Personal Fouls Per Game):场均个人犯规

  • PTS (Points Per Game):场均得分

处理数据

# 球员名字取\前面的字符
nba['Player'] = nba['Player'].apply(lambda x: x.split('\\')[0])
# 取控卫PG的样本进行分析
point_guards = nba[nba['Pos'] == 'PG']
# 剔除0失误的控卫数据,0tov意味着场次太少,且0不能被除
point_guards = point_guards[point_guards['TOV'] != 0]
# 定义ATR为助攻失误比,并计算出来
point_guards['ATR'] = point_guards['AST'] / point_guards['TOV']

处理后的数据变成了下面的样子,我们剩下了 124 个控卫数据。

判断一个控卫优不优秀,最重要的两个指标是场均得分和场均助攻失误比,也就是PTSATR。下面我们就用这两个关键特征对球员聚类。

探索数据

看下这些球员的场次得分与助攻失误比散点图,这往往是数据分析和建模的第一步。

# 改善下绘图风格
import seaborn as sns
sns.set()# nba联盟控卫场次得分与助攻失误比散点图
plt.scatter(point_guards['PTS'], point_guards['ATR'], c='y')
plt.title("Point Guards")
plt.xlabel('Points Per Game', fontsize=13)
plt.ylabel('Assist Turnover Ratio', fontsize=13)
plt.show()

最右边得分超过 30 的你不用猜,就是这个男人。

确认下数据,场均得分大于 25 的过滤出来看下,果然是库里,东契奇、欧文、利拉德等人赫然在列。这些超巨得分是高,但看起来助攻失误比差不多都在平均线上下。这是合理的。其实有点干的越多错的越多的意思,工作上亦是如此。

看了下助攻失误比超高或超低的,我都不认识就不说了。

我们聚类吧

从散点图来看,其实并没有明显的簇,但我们可以人为定义任意个簇。我们还是分成 5 类。于是我们开始做聚类的第一步。

STEP1:随机认定 5 个样本点作为簇的中心点

num_clusters = 5
# 从样本里随机选5个出来作为5个簇的起始中心点
random_initial_points = np.random.choice(point_guards.index, size=num_clusters)
centroids = point_guards.loc[random_initial_points]
# 画出散点图,包括5个随机选取的聚类中心点
plt.scatter(point_guards['PTS'], point_guards['ATR'], c='y')
plt.scatter(centroids['PTS'], centroids['ATR'], c='red')
plt.title("Centroids")
plt.xlabel('Points Per Game', fontsize=13)
plt.ylabel('Assist Turnover Ratio', fontsize=13)
plt.show()

随后就是不断迭代优化簇中心点的过程,为了方便,我们将中心点的坐标存在一个字典里。

def centroids_to_dict(centroids):dictionary = dict()# iterating counter we use to generate a cluster_idcounter = 0# iterate a pandas data frame row-wise using .iterrows()for index, row in centroids.iterrows():coordinates = [row['PTS'], row['ATR']]dictionary[counter] = coordinatescounter += 1return dictionarycentroids_dict = centroids_to_dict(centroids)

上图我们看到,随机选出来的centroids,我们把它存在了一个centroids_dict里面。

STEP2:将样本分配至离其最近的簇中心点上去

这里涉及两个计算,一个是距离的度量,一个是最小元素的查找。前者我们采用欧拉距离,后者是选择排序的精髓。

先定义好这两个计算函数,如下。

import mathdef calculate_distance(centroid, player_values):root_distance = 0for x in range(0, len(centroid)):difference = centroid[x] - player_values[x]squared_difference = difference**2root_distance += squared_differenceeuclid_distance = math.sqrt(root_distance)return euclid_distancedef assign_to_cluster(row):lowest_distance = -1closest_cluster = -1for cluster_id, centroid in centroids_dict.items():df_row = [row['PTS'], row['ATR']]euclidean_distance = calculate_distance(centroid, df_row)if lowest_distance == -1:lowest_distance = euclidean_distanceclosest_cluster = cluster_idelif euclidean_distance < lowest_distance:lowest_distance = euclidean_distanceclosest_cluster = cluster_idreturn closest_cluster

执行这两个函数,我们就完成了 STEP2,我们可视化看下完成后的结果。

point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)def visualize_clusters(df, num_clusters):colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']for n in range(num_clusters):clustered_df = df[df['cluster'] == n]plt.scatter(clustered_df['PTS'], clustered_df['ATR'], c=colors[n-1])plt.xlabel('Points Per Game', fontsize=13)plt.ylabel('Assist Turnover Ratio', fontsize=13)plt.show()visualize_clusters(point_guards, 5)

以上,5 个类别分别以不同的颜色标识出来了,但显然这是随机簇的结果,划分的 5 类结果并没有太好。我们还需要 STEP3。

STEP3:将簇中心点设置为所有点坐标的平均值

def recalculate_centroids(df):new_centroids_dict = dict()for cluster_id in range(0, num_clusters):values_in_cluster = df[df['cluster'] == cluster_id]# Calculate new centroid using mean of values in the clusternew_centroid = [np.average(values_in_cluster['PTS']), np.average(values_in_cluster['ATR'])]new_centroids_dict[cluster_id] = new_centroidreturn new_centroids_dict

STEP4:重复 2 和 3

# 多次迭代,试试100轮吧
for _ in range(0,100):centroids_dict = recalculate_centroids(point_guards)point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)visualize_clusters(point_guards, num_clusters)

最终的结果如上,我们看到效果已经得到了很好的优化。高得分的在一个簇,高助攻失误比的在一个簇。

以上,我们写了很多代码。

手写算法是为了学习和理解。工程上,我们要充分利用工具和资源。

sklearn 库就包含了我们常用的机器学习算法实现,可以直接用来建模。

from sklearn.cluster import KMeanskmeans = KMeans(n_clusters=num_clusters)
kmeans.fit(point_guards[['PTS', 'ATR']])
point_guards['cluster'] = kmeans.labels_visualize_clusters(point_guards, num_clusters)

短短五行代码就完成了我们从零开始写的百来行代码,效果看起来也很合理。值得说明的是,聚类受起始点的影响,可能不会达到全局最优结果。细心的朋友一定看出来了,上面两个最终聚类结果是有差异的。

02

风控中的应用

如果学习了一项技能,但是不知道怎么用,那就毫无意义。

想想风控中聚类可以用来干什么呢?风控中我们有什么数据,关注什么结果呢?

一个有意思的课题是,用户手机安装的 app 能不能区分用户的风险。答案显然是肯定的,

除了必要的社交、支付、生活和工具类 app 外,那些差异化的 app 偏好显然刻画不同类型的用户。安装了很多小贷平台的用户,就很可能是一个多头客户,在想着办法撸口子。

于是我们可以用 app 的安装情况来给用户聚类,假设采集了用户在 100 个 app 的安装情况,就可以对这 100 个 0、1 变量聚类。

聚成两类后,我们可以采用其中有标签的用户进行验证,如果好坏用户绝大部分都正确地被划分开了,那么有理由相信那些没有标签的用户大概率就等于簇的标签。

我没有对应的数据可以举例,但有类似的数据来说明问题。下面是美国国会的数据,party 代表了议员的党派,R 是共和党 Republican,D 是民主党 Democratic,I 是中间党派。其余数据均为 0、1 变量。这里的 party 就类似风控中的好坏用户。

import pandas as pd
votes = pd.read_csv("114_congress.csv")
votes.head()

于是我们开始无监督聚类。注意以下并未用到 party 这个标签。

# 用k-means clustering方法
from sklearn.cluster import KMeanskmeans_model = KMeans(n_clusters=2, random_state=1)
senator_distances = kmeans_model.fit_transform(votes.iloc[:, 3:])labels = kmeans_model.labels_

我们需要用标签去对聚类结果做验证。

可以看到,分成了 0 和 1 两个簇中,0 簇中 43 个议员有 41 个都是民主党,2 是无党派人士;1 簇中 57 个议员有 54 个都是共和党,3 个是民主党。数据总有异常,或者用户总有一部分比较奇怪,我们不追求 100%准确。

不完美才是人生的最完美。

推荐阅读

Pandas处理数据太慢,来试试Polars吧!

懒人必备!只需一行代码,就能导入所有的Python库

绝!关于pip的15个使用小技巧

介绍10个常用的Python内置函数,99.99%的人都在用!

可能是全网最完整的 Python 操作 Excel库总结!

秀,用NBA球员数据学透K-Means聚类相关推荐

  1. 基于k-means聚类算法对NBA球员数据的一次聚类分析

    数据挖掘大作业 前言 本章工具 k-means介绍 k-means原理 最佳k值的确定 拐点法 轮廓系数法 聚类运算 结果分析 小结 参考文献 前言 聚类分析的研究成果主要集中在基于距离(或者称为基于 ...

  2. scrapy爬虫储存到mysql_详解Python之Scrapy爬虫教程NBA球员数据存放到Mysql数据库

    获取要爬取的URL 爬虫前期工作 用Pycharm打开项目开始写爬虫文件 字段文件items # Define here the models for your scraped items # # S ...

  3. NBA球员数据爬虫练习

    (每周爬虫)NBA球员数据爬虫 准备开个新坑,一周练习一次小爬虫,对于质量较高的数据集,可以顺便做一下分析.同时回归Python代码与统计分析方法. url:https://nba.hupu.com/ ...

  4. 用python做一个数据查询软件_使用Python实现NBA球员数据查询小程序功能

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 以下文章来源于早起Python ,作者投稿君 一.前言 有时将代码转成带有界面的程序,会极大地方便 ...

  5. python进行数据查询_使用Python实现NBA球员数据查询小程序功能

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 以下文章来源于早起Python ,作者投稿君 一.前言 有时将代码转成带有界面的程序,会极大地方便 ...

  6. 爬取NBA球员数据画出球员的雷达图

    目录 爬取NBA球员数据并进行清洗 画出两个球员的雷达图 创建时间轴组件自动以时间轴播放 代码整理: 总结 爬取NBA球员数据并进行清洗 这里我是从https://nba.hupu.com/playe ...

  7. OpenCV的k - means聚类 -对图片进行颜色量化

    OpenCV的k - means聚类 目标 学习使用cv2.kmeans()数据聚类函数OpenCV 理解参数 输入参数 样品:它应该的np.float32数据类型,每个特性应该被放在一个单独的列. ...

  8. OpenCV官方文档 理解k - means聚类

    理解k - means聚类 目标 在这一章中,我们将了解k - means聚类的概念,它是如何工作等. 理论 我们将这个处理是常用的一个例子. t恤尺寸问题 考虑一个公司要发布一个新模型的t恤. 显然 ...

  9. 以nba球员数据学习聚类算法

    本内容所有代码都在我的github上,喜欢的朋友可以点个赞 https://github.com/zxhjames/NBA_DataAnalysis 由于一些小需求,需要学习下聚类算法,大二有段时间曾 ...

最新文章

  1. 如何克服实施OKR的阻力?
  2. Spark系列(八)Worker工作原理
  3. 何晓群pdf 应用回归分析第五版_暨南社会学论坛|未成年人司法中的法律实证与统计应用:多元回归分析(第三期)...
  4. 第2章 信号、接口和引脚(XIlinx ZYNQ-7000 SOC UG-585文档)
  5. charles 手机抓包设置
  6. [leetcode]541.反转字符串||
  7. 20万DBA都在关注的12个问题
  8. 苹果汽车项目团队添猛将?外媒称Apple Watch负责人已调入
  9. springboot酒店管理信息系统答辩PPT模板
  10. 你被大数据“杀熟”了么?
  11. 微波遥感SNAP(四)——检测地表沉降(2)相位解缠与地理编码
  12. python3 pyv8 linux,Python 3.4不能安装Pyv8模块
  13. 手把手教你搭建一台永久运行的个人服务器
  14. 基于华为巴龙MH5000-31 5G工业/商业模组开发(二)
  15. 修改html会影响seo,网站修改css影响seo吗?
  16. “创新雷神号”卫星成功发射,华为云分布式云原生“天地一体”首次组网成功
  17. 计算机教室英语手抄报,我的教室英语手抄报
  18. Matlab中的while循环
  19. 锐捷交换机配置MSTP以及VRRP
  20. 高光谱图像分类(一)入门

热门文章

  1. 面试:史上最全多线程面试题 - (锁内存模型线程)
  2. codeigniter在nginx安装配置及URL重写
  3. PHP进行生成并且导出CSV文件
  4. PHP金额计算高精度函数
  5. Linux之查看ubuntu版本
  6. 佳能单反相机二次开发包介绍_家用单反相机什么牌子好
  7. 指令引用的0x0000000内存 不能为written_浅谈虚拟机内存区
  8. JS实现倒计时三秒钟跳转到新的页面
  9. 在MySQL 5.7下排查内存泄露和OOM问题全过程
  10. c++ map是有序还是无序的_C++ STL中Map的按Key排序和按Value排序