第16届世界杯:激情与荣耀的足球盛宴
1197 2025-05-09
数据分析 - 关于NBA球员的数据分析
一、选题背景
由于喜爱篮球的有很多人,而喜爱篮球的朋友基本都看NBA,由于NBA拥有能够说全世界最好的球员,NBA引领了篮球这项运动的趋势,形成了篮球延伸出来的文明,即NBA文明。NBA在全世界有数以亿计的球迷,每年NBA的数据也都耐人寻味,本着对篮球的热爱和激情,对这些数据进行一个简单的数据分析,让大家了解到NBA赛后有趣的数据统计。
二、分析设计方案
数据内容:数据包括球员进球率,得分,犯规,出场时间, 薪资等进行分析
设计方案:数据读取,pyecharts基本图表等
三、数据分析步骤
数据源:来自公司同事给予的某年NBA篮球数据集
载入需要用到的模块:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
载入数据:
## 插入数据
data=pd.read_csv("C:\\Users\\60424\\Downloads\\nba_players_with_salary.csv")
data.head()
我们看得出数据集的前五列是按当年的得分榜排序的,分别是威少、詹皇、地表最强175、浓眉哥和考神。而数据包含39列,即不同维度的技术统计。而此份数据提供了这300+球员的众多项比赛数据。
## 描述统计
data.describe()
从数据中看几项比较重要的信息:
球员平均年龄为26.4岁,年龄段在19-38岁;
球员平均年薪为730万美金,当时最大的合同为年薪3000万美金;
球员平均出场时间为21.5分钟,某球员场均出场37.8分钟领跑联盟,当然也有只出场2.2分钟的角色球员,机会来之不易。
身高和体重分布
data['Height'] = data['Height'].str.replace('cm','')
data['Weight'] = data['Weight'].str.replace('kg','')
data['Height']
data['Height'] = data['Height'].astype('int')data['Weight'] = data['Weight'].astype('int')
data['Weight'].describe()
import matplotlib.pyplot as plt#解决中文显示问题plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定默认字体plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题# 使用直方图显示身高数值分布data['Weight'].plot(kind='hist')
#分析身高和得分是否有关
player.plot(kind='scatter',x='Height',y='Rating')
效率相关性分析
在众多的数据中,有一项名为“RPM”,标识球员的效率值,该数据反映球员在场时对球队比赛获胜的贡献大小,最能反映球员的综合实力。我们来看一下它与其他数据的相关性:
dat_cor=data.loc[:,['RPM','AGE','SALARY_MILLIONS','ORB','DRB','TRB','AST','STL','BLK','TOV','PF','POINTS','GP','MPG','ORPM','DRPM']]
coor=dat_cor.corr()
sns.heatmap(coor,square=True, linewidths=0.02, annot=False)
#seaborn中的heatmap函数,是将多维度数值变量按数值大小进行交叉热图展示。
由相关性分析的heatmap图可以看出,RPM值与年龄的相关性最弱,与“进攻效率值”、“场均得分”、“场均抢断数”等比赛技术数据的相关性最强。
我在接下来的分析中将把RPM作为评价一个球员能力及状态的直观反应因素之一。
球员数据分析
此处练习了一下pandas基本的数据框相关操作,包括提取部分列、head()展示、排序等,简单通过几个维度的展示,笼统地看一下16-17赛季那些球员冲在联盟的最前头。
#薪资最高的10名运动员
data.loc[:,['PLAYER','SALARY_MILLIONS','RPM','AGE','MPG']].sort_values(by='SALARY_MILLIONS',ascending=False).head(10)
#效率值最高的10名运动员
data.loc[:,['PLAYER','RPM','SALARY_MILLIONS','AGE','MPG']].sort_values(by='RPM',ascending=False).head(10)
#出场时间最高的10名运动员
data.loc[:,['PLAYER','RPM','SALARY_MILLIONS','AGE','MPG']].sort_values(by='MPG',ascending=False).head(10)
詹姆斯为该赛季薪水最高的球员,麦克康利拿到了大合同,但是在群星璀璨的薪金榜单上略显黯淡。同样出现在榜单的还有威少、哈登、杜兰特等球星。
我们先利用seaborn中的distplot绘图来分别看一下球员薪水、效率值、年龄这三个信息的分布情况:
#分布及核密度展示
sns.set_style('darkgrid') #设置seaborn的面板风格
plt.figure(figsize=(12,12))
plt.subplot(3,1,1) #拆分页面,多图展示
sns.distplot(data['SALARY_MILLIONS'])
plt.xticks(np.linspace(0,40,9))
plt.ylabel(u'$Salary$',size=10)
plt.subplot(3,1,2)
sns.distplot(data['RPM'])
plt.xticks(np.linspace(-10,10,9))
plt.ylabel(u'$RPM$',size=10)
plt.subplot(3,1,3)
sns.distplot(data['AGE'])
plt.xticks(np.linspace(20,40,11))
plt.ylabel(u'$AGE$',size=10)
可见年龄和效率值更符合正态分布,而球员薪水更像一个偏态分布,拿高薪的球员占据较小的比例。那么这些变量之间是否有什么隐藏的关系呢
双变量
dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']]
sns.jointplot(dat1.SALARY_MILLIONS,dat1.AGE,kind='kde',size=8)
上图展示的是球员薪水与年龄的关系,采用不同的kind方式(等高线图/hex/散点等),我们可以整体感受一下年龄和薪水的集中特点。
dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']]
sns.pairplot(dat1) #相关性展示,斜对角为分布展示,可以直观地看变量是否具有现行关系
上图展示的是球员薪水、效率值、年龄及场均得分四个变量间的两两相关关系,对角线展示的是本身的分布图,由散点的趋势我们可以看出不同特征的相关程度。整体看各维度的相关性都不是很强,正负值与薪水和场均得分呈较弱的正相关性,而年龄这一属性和其他的变量相关性较弱,究竟是家有一老如有一宝还是廉颇老矣。在已有的数据集里想要生成新的变量,例如:把球员按年龄分为老中青三代,可以借助定义一个函数,再利用apply的方式,生成新的变量。
#根据已有变量生成新的变量
data['avg_point']=data['POINTS']/data['MP'] #每分钟得分
def age_cut(df):
if df.AGE<=24:
return 'young'
elif df.AGE>=30:
return 'old'
else:
return 'best'
data['age_cut']=data.apply(lambda x: age_cut(x),axis=1) #球员是否处于黄金年龄
data['cnt']=1 #计数用
同样的目的,也可以使用numpy模块中的函数np.where,与excel中的if函数和R语言中的ifelse函数几乎是一样的,非常简便。
既然得到了老中青三代的标签,我们来看一下不同年龄段球员的RPM(正负值)与薪水之前的关系如何:
### 球员薪水与效率值 按年龄段来看
sns.set_style('darkgrid') #设置seaborn的面板风格
plt.figure(figsize=(8,8))
plt.title(u'$RPM\ and\ SALARY$',size=15)
X1=data.loc[data.age_cut=='old'].SALARY_MILLIONS
Y1=data.loc[data.age_cut=='old'].RPM
X2=data.loc[data.age_cut=='best'].SALARY_MILLIONS
Y2=data.loc[data.age_cut=='best'].RPM
X3=data.loc[data.age_cut=='young'].SALARY_MILLIONS
Y3=data.loc[data.age_cut=='young'].RPM
plt.plot(X1,Y1,'.')
plt.plot(X2,Y2,'.')
plt.plot(X3,Y3,'.')
plt.xlim(0,30)
plt.ylim(-8,8)
plt.xlabel('Salary',size=10)
plt.ylabel('RPM',size=10)
plt.xticks(np.arange(0,30,3))
plt.legend(['old','best','young'])
绝大部分的年轻球员拿着较低的薪水,数据非常集中。有两个离群点,是上文提到的戈贝尔和约基奇,两个小兄弟前途无量啊。黄金年龄的球员和老球员的数据相对发散,黄金年龄球员薪水与效率值正相关性更强。第一集团有几个全明星排头兵。老球员过了呼风唤雨的年纪,运动状态有所下滑,“高薪低效”的球员也稍微多一些。
球队薪资排行
将数据按球队分组,平均薪水降序排列,看一下联盟十大土豪球队:
### 分组操作 按球队
dat_grp=data.groupby(by=['TEAM'],as_index=False).agg({'SALARY_MILLIONS':np.mean,'RPM':np.mean,'PLAYER':np.size})
dat_grp=dat_grp.loc[dat_grp.PLAYER>5] #不考虑在赛季中转会的球员
dat_grp.sort_values(by='SALARY_MILLIONS',ascending=False).head(10)
我按照分球队分年龄段,上榜球员降序排列,如上榜球员数相同,则按效率值降序排列。
### 分组操作 按场上位置
dat_grp2=data.groupby(by=['TEAM','age_cut'],as_index=False).agg({'SALARY_MILLIONS':np.mean,'RPM':np.mean,'PLAYER':np.size})
dat_grp2=dat_grp2.loc[dat_grp2.PLAYER>3] ##剔除掉少量的position摇摆人
dat_grp2.sort_values(by=['PLAYER','RPM'],ascending=False).head(15)
按照效率值降序排列前10名球队的相关信息如下:
##数据可视化 按球队
dat_grp3=data.groupby(by=['TEAM'],as_index=False).agg({'SALARY_MILLIONS':np.mean,'RPM':np.mean,'PLAYER':np.size,'POINTS':np.mean,'eFG%':np.mean,'MPG':np.mean,'AGE':np.mean})
dat_grp3=dat_grp3.loc[dat_grp3.PLAYER>5]
dat_grp3.sort_values(by=['RPM'],ascending=False).head(10
利用箱线图和小提琴图看着10支球队的相关数据
sns.set_style('whitegrid')#设置seaborn的面板风格
plt.figure(figsize=(12,8))
dat_grp4=data[data['TEAM'].isin(['GS','CLE','SA','LAC','OKC','UTAH','CHA','TOR','NO','BOS'])]
plt.subplot(3,1,1)
sns.boxplot(x='TEAM',y='AGE',data=dat_grp4)
plt.subplot(3,1,2)
sns.boxplot(x='TEAM',y='SALARY_MILLIONS',data=dat_grp4)
plt.subplot(3,1,3)
sns.boxplot(x='TEAM',y='MPG',data=dat_grp4)
plt.figure(figsize=(12,8))
plt.subplot(3,1,1)
sns.violinplot(x='TEAM',y='POINTS',data=dat_grp4)
plt.subplot(3,1,2)
sns.violinplot(x='TEAM',y='eFG%',data=dat_grp4)
plt.subplot(3,1,3)
sns.violinplot(x='TEAM',y='RPM',data=dat_grp4)
从年龄结构看,老马刺年龄跨度最大,年龄中位数最高。猛龙队最年轻且年龄跨度最小,后劲十足。
从球队薪金看,勇士和骑士最高,俄村雷霆在失去杜兰特后栽了大跟头,薪金健康情况堪忧。
从出场时间看,骑士队最高且跨度低,小团体战斗能力出众。
从得分来看,骑士和勇士整体出众。雷霆的威少、绿军的小托马斯、醍醐的浓眉哥以及马刺的伦纳德均是各队的离群点,双拳难敌四手。
从命中率看,命中率各队非常集中,绿凯的小托马刺作为地表最强175远远高于其他人。
从效率值看,骑士和勇士是大赢家。各个队的离群点我们甚至不用通过具体的查询就可以猜到是哪位球员。
作为老牌劲旅,有一个球员时负离群点,查询:
data.loc[data.TEAM=='SA'].sort_values(by='RPM',ascending=True).head(3)
球员在场有分摊许多位置,以得分后卫和中锋为例进行对比得分能力
point_guards = nba[nba['pos'] == 'PG']point_guards['ppg'] = point_guards['pts'] / point_guards['g']#ppg =pts/gpoint_guards[['pts', 'g', 'ppg']].head(5)
point_guards = point_guards[point_guards['tov'] != 0]
point_guards['atr'] = point_guards['ast'] / point_guards['tov']
plt.scatter(point_guards['ppg'], 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()
num_clusters = 5
random_initial_points = np.random.choice(point_guards.index, size=num_clusters)
centroids = point_guards.loc[random_initial_points]
plt.scatter(point_guards['ppg'], point_guards['atr'], c='yellow')
plt.scatter(centroids['ppg'], 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_id
counter = 0
# iterate a pandas data frame row-wise using .iterrows()
for index, row in centroids.iterrows():
coordinates = [row['ppg'], row['atr']]
dictionary[counter] = coordinates
counter += 1
return dictionary
centroids_dict = centroids_to_dict(centroids)
import math
def calculate_distance(centroid, player_values):
root_distance = 0
for x in range(0, len(centroid)):
difference = centroid[x] - player_values[x]
squared_difference = difference**2
root_distance += squared_difference
euclid_distance = math.sqrt(root_distance)
return euclid_distance
q = [5, 2]
p = [3,1]
# Sqrt(5) = ~2.24
print(calculate_distance(q, p))
def assign_to_cluster(row):
lowest_distance = -1
closest_cluster = -1
for cluster_id, centroid in centroids_dict.items():
df_row = [row['ppg'], row['atr']]
euclidean_distance = calculate_distance(centroid, df_row)
if lowest_distance == -1:
lowest_distance = euclidean_distance
closest_cluster = cluster_id
elif euclidean_distance < lowest_distance:
lowest_distance = euclidean_distance
closest_cluster = cluster_id
return closest_cluster
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['ppg'], 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)
def recalculate_centroids(df):
new_centroids_dict = dict()
# 0..1...2...3...4
for cluster_id in range(0, num_clusters):
# Finish the logic
return new_centroids_dict
centroids_dict = recalculate_centroids(point_guards)
point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)
centroids_dict = recalculate_centroids(point_guards)
point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=num_clusters)
kmeans.fit(point_guards[['ppg', 'atr']])
point_guards['cluster'] = kmeans.labels_
visualize_clusters(point_guards, num_clusters)
动态可视化
plotly_express可以实现强大的动态可视化动能(以之前雷霆三少的数据为例):
## 聚合
grp=pd.groupby(data,['name','season','team','team_op'],as_index=False).agg({'mp':np.mean,'pts':np.mean,'ast':np.mean,'trb':np.mean,'tov':np.mean,})
grp.head()
grp=grp[(grp['season']!='07-08')&(grp['season']!='08-09')&(grp['season']!='19-20')]
tmp=grp#[grp['name']=='拉塞尔-威斯布鲁克'] #'凯文-杜兰特'#'詹姆斯-哈登' #拉塞尔-威斯布鲁克
px.scatter(tmp,x='ast',y='trb',color='team_op',size='pts',size_max=50,
title='雷霆三少',text='team_op',
facet_col='name',
animation_frame='season',animation_group='name',range_x=[0,20],range_y=[0,20])
tmp=pd.groupby(data,['name','season'],as_index=False).agg({'mp':np.mean,'pts':np.mean,'ast':np.mean,'trb':np.mean,'tov':np.mean,})
px.scatter_3d(tmp,x='ast',y='trb',z='pts',size='mp',size_max=50,
title='雷霆三少',text='name',
animation_frame='season',animation_group='name',range_x=[0,15],range_y=[0,20],range_z=[10,40])
NBA比赛战火不断!而随着科技的进步我们可以更好的对篮球比赛的数据进行记录和分析,这使得我们能更好地理解篮球,理解球员,结合我们的专业知识和兴趣爱好,更好地享受篮球比赛的无穷魅力。
1 import numpy as np
2 import pandas as pd
3 import matplotlib.pyplot as plt
4 import seaborn as sns
5 载入数据:
6
7 ## 插入数据
8 data=pd.read_csv("C:\\Users\\60424\\Downloads\\nba_players_with_salary.csv")
9 data.head()
10
11
12 ## 描述统计
13 data.describe()
14
15
16 data['Height'] = data['Height'].str.replace('cm','')
17 data['Weight'] = data['Weight'].str.replace('kg','')
18 data['Height']
19 data['Height'] = data['Height'].astype('int')
20 data['Weight'] = data['Weight'].astype('int')
21
22 data['Weight'].describe()
23
24 import matplotlib.pyplot as plt
25 #解决中文显示问题
26 plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定默认字体
27 plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
28 # 使用直方图显示身高数值分布
29 data['Weight'].plot(kind='hist')
30
31 #分析身高和得分是否有关
32
33 player.plot(kind='scatter',x='Height',y='Rating')
34
35
36
37 dat_cor=data.loc[:,['RPM','AGE','SALARY_MILLIONS','ORB','DRB','TRB','AST','STL','BLK','TOV','PF','POINTS','GP','MPG','ORPM','DRPM']]
38 coor=dat_cor.corr()
39 sns.heatmap(coor,square=True, linewidths=0.02, annot=False)
40 #seaborn中的heatmap函数,是将多维度数值变量按数值大小进行交叉热图展示。
41
42
43
44 #薪资最高的10名运动员
45 data.loc[:,['PLAYER','SALARY_MILLIONS','RPM','AGE','MPG']].sort_values(by='SALARY_MILLIONS',ascending=False).head(10)
46 #效率值最高的10名运动员
47 data.loc[:,['PLAYER','RPM','SALARY_MILLIONS','AGE','MPG']].sort_values(by='RPM',ascending=False).head(10)
48 #出场时间最高的10名运动员
49 data.loc[:,['PLAYER','RPM','SALARY_MILLIONS','AGE','MPG']].sort_values(by='MPG',ascending=False).head(10)
50
51
52 #分布及核密度展示
53 sns.set_style('darkgrid') #设置seaborn的面板风格
54 plt.figure(figsize=(12,12))
55 plt.subplot(3,1,1) #拆分页面,多图展示
56 sns.distplot(data['SALARY_MILLIONS'])
57 plt.xticks(np.linspace(0,40,9))
58 plt.ylabel(u'$Salary$',size=10)
59
60 plt.subplot(3,1,2)
61 sns.distplot(data['RPM'])
62 plt.xticks(np.linspace(-10,10,9))
63 plt.ylabel(u'$RPM$',size=10)
64
65 plt.subplot(3,1,3)
66 sns.distplot(data['AGE'])
67 plt.xticks(np.linspace(20,40,11))
68 plt.ylabel(u'$AGE$',size=10)
69
70
71
72
73 dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']]
74 sns.jointplot(dat1.SALARY_MILLIONS,dat1.AGE,kind='kde',size=8)
75
76
77 dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']]
78 sns.pairplot(dat1) #相关性展示,斜对角为分布展示,可以直观地看变量是否具有现行关系
79
80
81
82 #根据已有变量生成新的变量
83 data['avg_point']=data['POINTS']/data['MP'] #每分钟得分
84 def age_cut(df):
85 if df.AGE<=24:
86 return 'young'
87 elif df.AGE>=30:
88 return 'old'
89 else:
90 return 'best'
91 data['age_cut']=data.apply(lambda x: age_cut(x),axis=1) #球员是否处于黄金年龄
92 data['cnt']=1 #计数用
93
94 ### 球员薪水与效率值 按年龄段来看
95 sns.set_style('darkgrid') #设置seaborn的面板风格
96 plt.figure(figsize=(8,8))
97 plt.title(u'$RPM\ and\ SALARY$',size=15)
98 X1=data.loc[data.age_cut=='old'].SALARY_MILLIONS
99 Y1=data.loc[data.age_cut=='old'].RPM
100 X2=data.loc[data.age_cut=='best'].SALARY_MILLIONS
101 Y2=data.loc[data.age_cut=='best'].RPM
102 X3=data.loc[data.age_cut=='young'].SALARY_MILLIONS
103 Y3=data.loc[data.age_cut=='young'].RPM
104 plt.plot(X1,Y1,'.')
105 plt.plot(X2,Y2,'.')
106 plt.plot(X3,Y3,'.')
107 plt.xlim(0,30)
108 plt.ylim(-8,8)
109 plt.xlabel('Salary',size=10)
110 plt.ylabel('RPM',size=10)
111 plt.xticks(np.arange(0,30,3))
112 plt.legend(['old','best','young'])
113
114
115
116
117 ### 分组操作 按球队
118 dat_grp=data.groupby(by=['TEAM'],as_index=False).agg({'SALARY_MILLIONS':np.mean,'RPM':np.mean,'PLAYER':np.size})
119 dat_grp=dat_grp.loc[dat_grp.PLAYER>5] #不考虑在赛季中转会的球员
120 dat_grp.sort_values(by='SALARY_MILLIONS',ascending=False).head(10)
121
122
123 ### 分组操作 按场上位置
124 dat_grp2=data.groupby(by=['TEAM','age_cut'],as_index=False).agg({'SALARY_MILLIONS':np.mean,'RPM':np.mean,'PLAYER':np.size})
125 dat_grp2=dat_grp2.loc[dat_grp2.PLAYER>3] ##剔除掉少量的position摇摆人
126 dat_grp2.sort_values(by=['PLAYER','RPM'],ascending=False).head(15)
127
128
129
130
131 ##数据可视化 按球队
132 dat_grp3=data.groupby(by=['TEAM'],as_index=False).agg({'SALARY_MILLIONS':np.mean,'RPM':np.mean,'PLAYER':np.size,'POINTS':np.mean,'eFG%':np.mean,'MPG':np.mean,'AGE':np.mean})
133 dat_grp3=dat_grp3.loc[dat_grp3.PLAYER>5]
134 dat_grp3.sort_values(by=['RPM'],ascending=False).head(10
135
136 sns.set_style('whitegrid')#设置seaborn的面板风格
137 plt.figure(figsize=(12,8))
138 dat_grp4=data[data['TEAM'].isin(['GS','CLE','SA','LAC','OKC','UTAH','CHA','TOR','NO','BOS'])]
139 plt.subplot(3,1,1)
140 sns.boxplot(x='TEAM',y='AGE',data=dat_grp4)
141 plt.subplot(3,1,2)
142 sns.boxplot(x='TEAM',y='SALARY_MILLIONS',data=dat_grp4)
143 plt.subplot(3,1,3)
144 sns.boxplot(x='TEAM',y='MPG',data=dat_grp4)
145
146
147 plt.figure(figsize=(12,8))
148 plt.subplot(3,1,1)
149 sns.violinplot(x='TEAM',y='POINTS',data=dat_grp4)
150 plt.subplot(3,1,2)
151 sns.violinplot(x='TEAM',y='eFG%',data=dat_grp4)
152 plt.subplot(3,1,3)
153 sns.violinplot(x='TEAM',y='RPM',data=dat_grp4)
154
155
156
157 data.loc[data.TEAM=='SA'].sort_values(by='RPM',ascending=True).head(3)
158
159
160 point_guards = nba[nba['pos'] == 'PG']
161 point_guards['ppg'] = point_guards['pts'] / point_guards['g']
162 #ppg =pts/g
163 point_guards[['pts', 'g', 'ppg']].head(5)
164
165
166
167
168
169 point_guards = point_guards[point_guards['tov'] != 0]
170 point_guards['atr'] = point_guards['ast'] / point_guards['tov']
171 plt.scatter(point_guards['ppg'], point_guards['atr'], c='y')
172 plt.title("Point Guards")
173 plt.xlabel('Points Per Game', fontsize=13)
174 plt.ylabel('Assist Turnover Ratio', fontsize=13)
175 plt.show()
176
177
178
179
180 num_clusters = 5
181
182 random_initial_points = np.random.choice(point_guards.index, size=num_clusters)
183
184 centroids = point_guards.loc[random_initial_points]
185 plt.scatter(point_guards['ppg'], point_guards['atr'], c='yellow')
186 plt.scatter(centroids['ppg'], centroids['atr'], c='red')
187 plt.title("Centroids")
188 plt.xlabel('Points Per Game', fontsize=13)
189 plt.ylabel('Assist Turnover Ratio', fontsize=13)
190 plt.show()
191
192
193
194
195 def centroids_to_dict(centroids):
196 dictionary = dict()
197 # iterating counter we use to generate a cluster_id
198 counter = 0
199
200 # iterate a pandas data frame row-wise using .iterrows()
201 for index, row in centroids.iterrows():
202 coordinates = [row['ppg'], row['atr']]
203 dictionary[counter] = coordinates
204 counter += 1
205
206 return dictionary
207
208 centroids_dict = centroids_to_dict(centroids)
209
210 import math
211
212 def calculate_distance(centroid, player_values):
213 root_distance = 0
214
215 for x in range(0, len(centroid)):
216 difference = centroid[x] - player_values[x]
217 squared_difference = difference**2
218 root_distance += squared_difference
219
220 euclid_distance = math.sqrt(root_distance)
221 return euclid_distance
222
223 q = [5, 2]
224 p = [3,1]
225
226 # Sqrt(5) = ~2.24
227 print(calculate_distance(q, p))
228
229
230
231
232 def assign_to_cluster(row):
233 lowest_distance = -1
234 closest_cluster = -1
235
236 for cluster_id, centroid in centroids_dict.items():
237 df_row = [row['ppg'], row['atr']]
238 euclidean_distance = calculate_distance(centroid, df_row)
239
240 if lowest_distance == -1:
241 lowest_distance = euclidean_distance
242 closest_cluster = cluster_id
243 elif euclidean_distance < lowest_distance:
244 lowest_distance = euclidean_distance
245 closest_cluster = cluster_id
246 return closest_cluster
247
248 point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)
249
250 def visualize_clusters(df, num_clusters):
251 colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
252
253 for n in range(num_clusters):
254 clustered_df = df[df['cluster'] == n]
255 plt.scatter(clustered_df['ppg'], clustered_df['atr'], c=colors[n-1])
256 plt.xlabel('Points Per Game', fontsize=13)
257 plt.ylabel('Assist Turnover Ratio', fontsize=13)
258 plt.show()
259
260 visualize_clusters(point_guards, 5)
261
262
263
264
265
266
267 def recalculate_centroids(df):
268 new_centroids_dict = dict()
269 # 0..1...2...3...4
270 for cluster_id in range(0, num_clusters):
271 # Finish the logic
272 return new_centroids_dict
273
274 centroids_dict = recalculate_centroids(point_guards)
275
276 point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)
277
278 centroids_dict = recalculate_centroids(point_guards)
279 point_guards['cluster'] = point_guards.apply(lambda row: assign_to_cluster(row), axis=1)
280
281 from sklearn.cluster import KMeans
282
283 kmeans = KMeans(n_clusters=num_clusters)
284 kmeans.fit(point_guards[['ppg', 'atr']])
285 point_guards['cluster'] = kmeans.labels_
286
287 visualize_clusters(point_guards, num_clusters)
288
289
290 ## 聚合
291 grp=pd.groupby(data,['name','season','team','team_op'],as_index=False).agg({'mp':np.mean,'pts':np.mean,'ast':np.mean,'trb':np.mean,'tov':np.mean,})
292 grp.head()
293 grp=grp[(grp['season']!='07-08')&(grp['season']!='08-09')&(grp['season']!='19-20')]
294 tmp=grp#[grp['name']=='拉塞尔-威斯布鲁克'] #'凯文-杜兰特'#'詹姆斯-哈登' #拉塞尔-威斯布鲁克
295
296 px.scatter(tmp,x='ast',y='trb',color='team_op',size='pts',size_max=50,
297 title='雷霆三少',text='team_op',
298 facet_col='name',
299 animation_frame='season',animation_group='name',range_x=[0,20],range_y=[0,20])
300
301
302
303
304 tmp=pd.groupby(data,['name','season'],as_index=False).agg({'mp':np.mean,'pts':np.mean,'ast':np.mean,'trb':np.mean,'tov':np.mean,})
305
306 px.scatter_3d(tmp,x='ast',y='trb',z='pts',size='mp',size_max=50,
307 title='雷霆三少',text='name',
308 animation_frame='season',animation_group='name',range_x=[0,15],range_y=[0,20],range_z=[10,40])