前面我们在制作热力图的时候使用了Seaborn, Seaborn是基于Matplotlib的图形可视化Python包。它提供了一种高度交互式界面,便于用户能够制作出各种有吸引力的统计图表。
Seaborn在Matplotlib的基础上进行了更高级的API封装,从而使得制图更加容易。在大多数情况下使用Matplotlib就能制作出很具有吸引力的图,而使用Seaborn能制作出具有更多特色的图。应该把Seaborn视为Matplotlib的补充,而不是替代物。同时,它能高度兼容NumPy与Pandas数据结构以及SciPy与Statsmodels等统计模式。
Seaborn要求原始数据的输入类型为Pandas的Dataframe或NumPy数组,画图函数有以下几种形式:
● sns.图名(x='X轴列名',y='Y轴列名',data=原始数据df对象)
● sns.图名(x='X轴列名',y='Y轴列名',hue='分组绘图参数',data=原始数据df对象)
● sns.图名(x=np.array, y=np.array[, ...])
● sns.barplot (x=np.array, y=np.array)
下面我们尝试使用Seaborn对数据集中球员的RPM、薪资和年龄进行一个单变量分析,完整代码如下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns pd.set_option('display.max_columns', None) data = pd.read_csv("./NBA data/球员薪资.csv") # 利用seaborn中的displot绘图来分别看一下球员的RPM、薪资、年龄这三个信息的分布情况 # 分布及核密度展示 sns.set_style('darkgrid') # 设置Seaborn的面板风格 # 获取画布 plt.figure(figsize=(10, 10)) # 拆分页面,多图展示 plt.subplot(3, 1, 1) # 绘制直方图图像 sns.distplot(data['SALARY_MILLIONS']) # 把0~40分成9个间隔(包含0和40) plt.xticks(np.linspace(0, 40, 9)) # y轴标签 plt.ylabel('Salary', size=10) # size:设置字体大小 # 拆分画布 plt.subplot(3, 1, 2) # 绘制直方图图像 sns.distplot(data['RPM']) plt.xticks(np.linspace(-10, 10, 9)) # y轴标签 plt.ylabel('RPM', size=10) # 拆分画布 plt.subplot(3, 1, 3) # 绘制直方图图像 sns.distplot(data['AGE']) plt.xticks(np.linspace(20, 40, 11)) # y轴标签 plt.ylabel('AGE', size=10) plt.show()
输出结果如图3.6所示。
图3.6
生成的结果是一个直方图,此种直方图的形式符合正态分布,而在本例中年龄和效率值也符合正态分布,球员薪资则更像一个偏态分布,拿高薪的球员占据较小的比例。
在上一小节中,我们看到RPM、薪资以及年龄的单元分析,这实际上与我们的想法基本一致,即拿高薪的是少数。而剩下的两个元素两两之间又有什么可能的关系呢?二元可视化分析代码如下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns pd.set_option('display.max_columns', None) data = pd.read_csv("./NBA data/球员薪资.csv") # 使用jointplot查看年龄和薪资之间的关系 dat1=data.loc[:,['RPM','SALARY_MILLIONS','AGE','POINTS']] sns.jointplot(dat1.SALARY_MILLIONS,dat1.AGE,kind='hex',size=8) plt.show()
结果如图3.7所示。
图3.7
这是一个二维的等高线分析结果,颜色越深表示在此位置的数据数目越多,即大部分球员集中在22~25岁拿到5百万以下的薪资。
在这里我们可以换一种展示方式,而对其更换的方式可以通过sns.jointplot函数中的kind参数进行设定,这里笔者准备了所有的kind类型:
plot_kinds = ["scatter", "hist", "hex", "kde", "reg", "resid"]
有兴趣的读者可以自行尝试。
而将多个变量组合在一起展示的方法代码如下:
multi_data = data.loc[:, ['RPM','SALARY_MILLIONS','AGE','POINTS']] sns.pairplot(multi_data) plt.show()
结果如图3.8所示。
图3.8展示的是球员PRM、薪资、年龄及场均得分四个变量间的两两相关关系,对角线展示的是本身的分布图,由散点的趋势可以看出不同特征的相关程度。
图3.8
前面我们对球员的各个属性进行了分析,整体来看各维度的相关性都不是很强,RPM与薪资和场均得分呈较弱的正相关性,而年龄这一属性和其他的变量相关性较弱,究竟是家有一老如有一宝还是廉颇老矣,接下来我们从年龄维度入手来进一步分析。
在这里我们根据球员年龄大小将其分成“老、中、青”三代,即我们定义小于24岁的为青年,24~30岁的为中年球员,而30岁以上的为老年球员,代码如下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns pd.set_option('display.max_columns', None) data = pd.read_csv("./NBA data/球员薪资.csv") # 根据已有变量生成新的变量 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 # 计数用 # 球员薪资与效率值 按年龄段来看 sns.set_style('darkgrid') # 设置Seaborn的面板风格 plt.figure(figsize=(8, 8), dpi=100) plt.title('RPM and SALARY', size=15) X1 = data.loc[data.age_cut == 'old'].SALARY_MILLIONS Y1 = data.loc[data.age_cut == 'old'].RPM plt.plot(X1, Y1, '.') X2 = data.loc[data.age_cut == 'best'].SALARY_MILLIONS Y2 = data.loc[data.age_cut == 'best'].RPM plt.plot(X2, Y2, '^') X3 = data.loc[data.age_cut == 'young'].SALARY_MILLIONS Y3 = data.loc[data.age_cut == 'young'].RPM plt.plot(X3, Y3, '.') plt.xlim(0, 30) plt.ylim(-8, 8) plt.xlabel('Salary') plt.ylabel('RPM') plt.xticks(np.arange(0, 30, 3)) # 绘制图例 plt.legend(['old', 'best', 'young']) # 显示图像 plt.show()
显示结果如图3.9所示。
图3.9
图3.9中横坐标为球员薪资,纵坐标为RPM。从图中可以得到如下结论:
● 绝大部分的年轻球员拿着较低的薪资,数据非常集中。
● 年轻球员有离群点,可能为特例。
● 黄金年龄的球员和老球员的数据相对发散,黄金年龄球员薪资与效率值正相关性更强。
● 老球员性价比不高。
除了RPM与薪资的比较,在这份数据集中还可以得到一些额外的展示,在这里我们采用更多的数据特征对其进行甄别分析,代码如下:
dat2=data.loc[:,['RPM','POINTS','TRB','AST','STL','BLK','age_cut']] sns.pairplot(dat2,hue='age_cut') multi_data2 = data.loc[:, ['RPM','POINTS','TRB','AST','STL','BLK','age_cut']] sns.pairplot(multi_data2, hue='age_cut') # 按照标签进行分类加颜色 plt.show()
结果请读者自行打印验证。
对于每个球队来说一个非常重要的内容就是本队队员的总体薪资水平,上面章节中我们主要对各个球员的薪资情况进行分析,下面我们以球队为单位对薪资进行统计。代码如下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns pd.set_option('display.max_columns', None) data = pd.read_csv("./NBA data/球员薪资.csv") # 分组操作 按球队分组 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] # 不考虑在赛季中转会的球员 print(dat_grp.sort_values(by='SALARY_MILLIONS', ascending=False).head(10))
在这里我们统计了全队的薪资水平以及全队的平均RPM,如图3.10所示。可以看到平均薪资最高的球队,其全队的平均RPM也是最高。
图3.10
而根据年龄结构排序则可以使用如下的分析代码:
# age_cut为自定义属性 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 # 计数用 dat_grp2=data.groupby(by=['TEAM','age_cut'],as_index=False).agg({'SALARY_MILLION S':np.mean,'RPM':np.mean,'PLAYER':np.size}) dat_grp2=dat_grp2.loc[dat_grp2.PLAYER>3] dat_grp2.sort_values(by=['PLAYER','RPM'],ascending=False).head(15)
具体结果请读者自行验证。
最后我们看一下每个球队的综合实力。在这里我们使用四分位图查看球队的信息,代码如下:
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.show()
最终的显示结果如图3.11所示。
图3.11
具体结果请读者自行分析。
当然在数据集中还蕴含更多的信息可以供读者挖掘和使用,笔者期望读者能够对其做出更好的分析与说明。