学习数据分析一月有余,终于鼓起勇气开启了自己的实战演练。本次练习的文件从BasketballReference 上下载,2016~16赛季球员数据,自定义了几个分析任务,通过R对数据框、字符、数字的操作,熟悉和掌握数据分析的基本方法。
本篇原本是在知乎上操作的。熬夜到2点写的文章因为知乎平台一个未知错误,导致图片代码全部消失,这里我只想说¥%(&%…,于是果断放弃知乎。
数据分析的一遍流程是:
业务理解=>数据准备/预处理=>分析/挖掘=>评估=>应用/部署/报告/决策
获取数据
表格的主要字段有球员姓名,位置,出场次数,首发次数等,如下图。
2016~16赛季球员数据明确目标
分析任务是:
- 算出得分最多的前十名
- 各个位置(PG,SG,SF,PF,C)的最佳球员
- 算出每个球队在16~17赛季的总得分
- 绘制年龄和命中率的趋势图
- 答案在最后
数据预处理
首先在windows下读取excel文件,这里有一些坑:
- 需要用到的包是openxlsx,但是有时会安装失败,这时就需要先安装Rtools
然后在R中运行
Sys.setenv("R_ZIPCMD" = "C:/Rtools/bin/zip.exe")
- library导入所需的包之后,openxlsx中的函数
read.xlsx()
可以很方便的读取文件,函数中的文件路径,可以是相对路径,亦可以是绝对路径。如果使用相对路径,相对的是当前的工作目录getwd()
可查看。后来发现可以使用file.choose()
来直接拉取windows的文件浏览器。
nbaData <- read.xlsx(file.choose())
下载的原始数据感觉不是美美哒,需要进行数据预处理,预处理的主要工作包含重命名列,处理缺失值,处理日期,数值转换,排序。
- 处理缺失值
函数na.omit()
用来删除数据框中含有缺失值的记录
nbaData <- na.omit(nbaData)
- 在原始数据中,姓名带斜杠,后面有一些标志:
好像是每个球员的某种唯一编码,不太懂。去掉好处理。
library(stringr)
fixednames <- str_split_fixed(nbaData$Player,'\\\\',n=2)[,1]
nbaData$Player <- fixednames
斜杠\
是R中的转义字符,使用\\\\\\\\
才能正确表达使用斜杠截取的含义,str_split_fixed
是stringr中的函数,看名字就能才出来它是str_split
的加强版,返回的是含有n个元素的矩阵。
- 这里遇到一个问题,某些球员如 Quincy Acy 先后在三个球队服役,这里为了方便处理,只保留第一个
nbaData <- nbaData[!duplicated(nbaData[,c("Player")]),]
计算关键指标
算出得分最多的前十名
各个位置(PG,SG,SF,PF,C)的最佳球员
每个位置的评判 标准不一样,比如控后PG就更看重助攻AST而不是篮板TRB,而中锋C的核心指标应该是篮板和盖帽BLK。况且每个字段的值域还都不一样,这就比较蛋疼了。我想到一个办法——标准化。
这里我们做一些简单的规定,对于每个位置,篮板数TRB、助攻AST、盖帽BLK、场均得分PS/G、失误PF,各自权重如下:
weightPG <- c(0,4,0,3,-1)
weightSG <- c(0,2,1,4,-2)
weightSF <- c(2,1,2,4,-1)
weightPF <- c(3,1,3,3,-1)
weightC <- c(4,2,4,3,-1)
可能不是很合理,凑合用吧先
发现严重问题
并不是每个人的位置都是固定的,比如杰弗瑞·洛文吉这货,就既是中锋,又是大前。我想知道还有那些人担任着多种角色,进行了如下尝试:
首先定义全部的位置
allpos <- c('PG','SG','SF','PF','C')
然后查看在所有球员中,哪些人的位置在这个向量里面is.element(nbaData$Pos,allpos)
返回的是一堆逻辑向量,在总表中
nbaData[!is.element(nbaData$Pos,allpos),]
输出的结果如下
Rk Player Pos Age Tm G GS MP FG FGA FG% 3P 3PA 3P%
310 254 Joffrey Lauvergne PF-C 25 TOT 70 1 14 2.1 4.8 0.44 0.5 1.4 0.337
2P 2PA 2P% eFG% FT FTA FT% ORB DRB TRB AST STL BLK TOV
310 1.6 3.4 0.483 0.49 0.7 1 0.63 1 2.6 3.6 1 0.4 0.1 0.8 1.2 5.4
score
310 378
就只有你一个。好吧去掉。
nbaData <- nbaData[is.element(nbaData$Pos,allpos),]
把所有的球员按位置分组
PG <- nbaData[nbaData$Pos=='PG',]
SG <- nbaData[nbaData$Pos=='SG',]
SF <- nbaData[nbaData$Pos=='SF',]
PF <- nbaData[nbaData$Pos=='PF',]
C <- nbaData[nbaData$Pos=='C',]
然后用函数scale
算出他们篮板数TRB、助攻AST、盖帽BLK、场均得分PS/G、失误PF的标准差,与权重相乘,求和,是他们的最终得分。
基于每个位置的方法大同小异,我以控球后卫PG为例。
valueName <-c('TRB','AST','BLK','PS/G','PF')
sd <- t(t(scale(PG[valueName ]))*weightPG)
PG$est<- round(apply(sd,1,sum),digits=2)
PG <- PG[order(PG$est,decreasing = T),]
firstPG <- PG[1,]
好吧,这样算下来还是威斯布鲁克。
按这个步骤算出其他位置:
firstSG <- 'DeMar DeRozan'
firstSF <- 'Kevin Durant'
firstPF <- 'Kristaps Porzingis'
firstC <- 'Anthony Davis'
除了杜兰特,其他人是谁?
算出每个球队在16~17赛季的总得分
数据表中只有每个球员的得分,和所在球队,我要做的就是根据球队,计算球员的总得分,我想到了可以用SQL中的SELECT sum(score) FROM nbaData GROUP BY Tm
的方法,不过既然要学习R,就要用R的方法,找了一早上,发现了tapply
这个函数可以进行分组计算。
teamscore <- tapply(nbaData$score, nbaData$Tm, sum)
然后进行绘图
- paste函数默认以空格分隔,设置
sep=""
可以避免空格 - text函数用来为plot图表添加坐标上的数值。
- X轴上的名字总是显示不全,谁能教教我?
filename <- paste('score of each team','.png',sep="")
png(file=filename)
barplot(height=teamscore,names.arg=teams,xlab="team",ylab="score",col="blue",
main="score of each team",border="red")
text(x,teamscore,labels=teamscore,cex=.7,pos=1,col='orange')
dev.off()
score of each team.png
很丑我知道。
绘制年龄和命中率的趋势图
计算命中率,图表中有2P%(2分球命中率)、3P%(3分球命中率),和FT%(罚篮命中率),罚球不参与讨论。2P%和3P%打算取平局值,能说明问题就行。
nbaData$AvePR <- apply(nbaData[c('2P%','3P%')],1,mean)
首次是求出了平均值,然后绘制散点图,并根据散点图拟合出回归线。起初使用attach和detach会报错The following objects are masked _by_.GlobalEnv:
查了一下原来是因为attach出来的变量有重名的,并推荐使用with。
with(nbaData,{
filename <- paste('各年龄球员命中率','.png',sep="")
png(file=filename)
plot(Age,AvePR,pch=16,xlab="年龄",ylab="命中率",col="blue",
main="各年龄球员命中率")
abline(lm(AvePR~Age))
dev.off()
})
猜一下那个年龄最大的球员是谁。
有点意外,命中率竟然和年龄没多大关系。
nbaData[nbaData$Age == max(nbaData$Age),]
答案
-
算出得分最多的前十名
他们是威斯布鲁克、 哈登、小托马斯 、安东尼·戴维斯、22岁的卡尔·安东尼·唐斯、利拉德 、德玛尔·德罗赞、库里、我詹 、德马库斯·考辛斯,不服来辩。 -
各个位置(PG,SG,SF,PF,C)的最佳球员
控后:威斯布鲁克
分后: 德玛尔·德罗赞
小前: 杜兰特
大前:克里斯塔普斯·波尔津吉斯
中锋: 安东尼·戴维斯 -
算出每个球队在16~17赛季的总得分
参看上面的图。 -
绘制年龄和命中率的趋势图
参看上一题。
总结
6月27号开始本次本次实践,中间断断续续发生一些事情,本不该影响到学习的,到做完已经花费10多天,是不是有点慢了?恩姆。好歹自己坚持下来做完了,小小一张表竟然蕴含了这么多信息,简直太有意思,体会到所谓冰山一角的含义了。所以,慢就是快。
网友评论