数据分析 - 关于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()

nba数据分析师主要是干嘛 nba数据模型_数据分析

我们看得出数据集的前五列是按当年的得分榜排序的,分别是威少、詹皇、地表最强175、浓眉哥和考神。而数据包含39列,即不同维度的技术统计。而此份数据提供了这300+球员的众多项比赛数据。

## 描述统计
data.describe()

nba数据分析师主要是干嘛 nba数据模型_nba数据分析师主要是干嘛_02

 

 

 

从数据中看几项比较重要的信息:

球员平均年龄为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')

 

 

 

 

 

nba数据分析师主要是干嘛 nba数据模型_数据分析_03

 

 

 

效率相关性分析

在众多的数据中,有一项名为“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函数,是将多维度数值变量按数值大小进行交叉热图展示。

 

nba数据分析师主要是干嘛 nba数据模型_ide_04

 

 

 

由相关性分析的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)

nba数据分析师主要是干嘛 nba数据模型_ide_05

 

 

詹姆斯为该赛季薪水最高的球员,麦克康利拿到了大合同,但是在群星璀璨的薪金榜单上略显黯淡。同样出现在榜单的还有威少、哈登、杜兰特等球星。

我们先利用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)

nba数据分析师主要是干嘛 nba数据模型_ide_06

可见年龄和效率值更符合正态分布,而球员薪水更像一个偏态分布,拿高薪的球员占据较小的比例。那么这些变量之间是否有什么隐藏的关系呢

双变量

dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']]
sns.jointplot(dat1.SALARY_MILLIONS,dat1.AGE,kind='kde',size=8)

  

nba数据分析师主要是干嘛 nba数据模型_数据_07

 

 

 上图展示的是球员薪水与年龄的关系,采用不同的kind方式(等高线图/hex/散点等),我们可以整体感受一下年龄和薪水的集中特点。

dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']]
sns.pairplot(dat1) #相关性展示,斜对角为分布展示,可以直观地看变量是否具有现行关系

nba数据分析师主要是干嘛 nba数据模型_数据_08

 

 

 上图展示的是球员薪水、效率值、年龄及场均得分四个变量间的两两相关关系,对角线展示的是本身的分布图,由散点的趋势我们可以看出不同特征的相关程度。整体看各维度的相关性都不是很强,正负值与薪水和场均得分呈较弱的正相关性,而年龄这一属性和其他的变量相关性较弱,究竟是家有一老如有一宝还是廉颇老矣。在已有的数据集里想要生成新的变量,例如:把球员按年龄分为老中青三代,可以借助定义一个函数,再利用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'])

nba数据分析师主要是干嘛 nba数据模型_nba数据分析师主要是干嘛_09

 

 

 

绝大部分的年轻球员拿着较低的薪水,数据非常集中。有两个离群点,是上文提到的戈贝尔和约基奇,两个小兄弟前途无量啊。黄金年龄的球员和老球员的数据相对发散,黄金年龄球员薪水与效率值正相关性更强。第一集团有几个全明星排头兵。老球员过了呼风唤雨的年纪,运动状态有所下滑,“高薪低效”的球员也稍微多一些。

球队薪资排行

将数据按球队分组,平均薪水降序排列,看一下联盟十大土豪球队:

### 分组操作 按球队
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)

nba数据分析师主要是干嘛 nba数据模型_数据分析_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)

 

 

 

 

nba数据分析师主要是干嘛 nba数据模型_ide_11

 

 

 

按照效率值降序排列前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

nba数据分析师主要是干嘛 nba数据模型_数据_12

 

 

 利用箱线图和小提琴图看着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)

nba数据分析师主要是干嘛 nba数据模型_ide_13

 

 

 

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)

nba数据分析师主要是干嘛 nba数据模型_数据分析_14

从年龄结构看,老马刺年龄跨度最大,年龄中位数最高。猛龙队最年轻且年龄跨度最小,后劲十足。

从球队薪金看,勇士和骑士最高,俄村雷霆在失去杜兰特后栽了大跟头,薪金健康情况堪忧。

从出场时间看,骑士队最高且跨度低,小团体战斗能力出众。

从得分来看,骑士和勇士整体出众。雷霆的威少、绿军的小托马斯、醍醐的浓眉哥以及马刺的伦纳德均是各队的离群点,双拳难敌四手。

从命中率看,命中率各队非常集中,绿凯的小托马刺作为地表最强175远远高于其他人。

从效率值看,骑士和勇士是大赢家。各个队的离群点我们甚至不用通过具体的查询就可以猜到是哪位球员。

作为老牌劲旅,有一个球员时负离群点,查询:

data.loc[data.TEAM=='SA'].sort_values(by='RPM',ascending=True).head(3)

nba数据分析师主要是干嘛 nba数据模型_数据分析_15

球员在场有分摊许多位置,以得分后卫和中锋为例进行对比得分能力

point_guards = nba[nba['pos'] == 'PG']
point_guards['ppg'] = point_guards['pts'] / point_guards['g']
#ppg =pts/g
point_guards[['pts', 'g', 'ppg']].head(5)

nba数据分析师主要是干嘛 nba数据模型_nba数据分析师主要是干嘛_16

 

 

 

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()

nba数据分析师主要是干嘛 nba数据模型_数据分析_17

 

 

 

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()

 

nba数据分析师主要是干嘛 nba数据模型_数据分析_18

 

 

 

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))

nba数据分析师主要是干嘛 nba数据模型_数据分析_19

 

 

 

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)

  

nba数据分析师主要是干嘛 nba数据模型_nba数据分析师主要是干嘛_20

 

 

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)

  

nba数据分析师主要是干嘛 nba数据模型_nba数据分析师主要是干嘛_21

 

 

 动态可视化

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])

 

nba数据分析师主要是干嘛 nba数据模型_数据分析_22

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数据分析师主要是干嘛 nba数据模型_nba数据分析师主要是干嘛_23

 

 

 

 

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])