受新冠肺炎影响,2019-2020赛季NBA已经处于停摆状态,是否以及何时能复赛还不清楚。相关的各项评选如常规赛MVP、最佳阵容、最佳防守等也由于疫情暂停了。按照往年的赛程节奏,此时也应该进入常规赛收官阶段了。本文利用历史数据和本赛季常规赛已发生数据来预测本赛季(2019-2020赛季)的常规赛MVP。NBA常规赛MVP是NBA所有个人荣誉中的最高荣誉。像乔丹、科比、詹姆斯、库里等超级巨星曾经到获得过常规赛MVP。
一、数据和方法
- 数据:13-14赛季、14-15赛季、15-16赛季、16-17赛季、17-18、19-20赛季共6个赛季常规赛数据
- 方法:利用xgboost模型回归球员MVP得票率
1、xgboost可以做分类、回归、排序等任务,在R语言中通过设置objective参数进行明确
- “reg:linear” :线性回归
- “reg:logistic” :逻辑回归
- “binary:logistic” :二分类的逻辑回归问题,输出为概率
- “binary:logitraw” :二分类的逻辑回归问题,输出的结果为wTx
- “count:poisson” :计数问题的poisson回归,输出结果为poisson分布
- “multi:softmax” :让XGBoost采用softmax目标函数处理多分类问题,同时需要设置参数num_class(类别个数)
- “multi:softprob” :和softmax一样,但是输出的是ndata * nclass的向量,可以将该向量reshape成ndata行nclass列的矩阵。每行数据表示样本所属于每个类别的概率
- “rank:pairwise” :做排序任务
- 关于R语言实现xgboost模型的参数解释参考:https://blog.csdn.net/zc02051126/article/details/46711047
2、xgboost的输入变量必须是数值型类型,若是无序分类变量需要做one-hot预处理
- 对于类别有序的类别型变量,比如 age 等,当成数值型变量处理可以的。对于非类别有序的类别型变量,推荐 one-hot。但是 one-hot 会增加内存开销以及训练时间开销。
- 类别型变量在范围较小时(tqchen 给出的是[10,100]范围内)推荐使用
- 原始数据字段:
出场次数 | 首发次数 | 出场时间 | 投篮命中率 | 命中个数 | 出手次数 |
三分命中率 | 三分命中个数 | 三分出手次数 | 罚球命中率 | 罚球命中个数 | 罚球出手次数 |
篮板数 | 前场篮板数 | 后场篮板数 | 助攻数 | 抢断数 | 盖帽数 |
失误数 | 犯规数 | 得分 | 胜场数 | 负场数 | mvp得票率* |
- 本文应用数据集:https://mp.csdn.net/console/upDetailed
二、建模与预测
1.数据预处理
(1)原始数据预处理:
- 由于本赛季并未进行完成,因此出场次数、首发次数、胜场数、负场数等受时间制约的特征需要进行标准化(此处适合用Min-Max的方式进行标准化)。因此在这里执行两个动作,一是取消首发次数、胜场数、负场数三个特征,新增首发率、出场胜率两个字段来表征,率指标可消除绝对值的时间因素影响;二是对每个赛季的出场次数特征进行标准化:
- 鉴于实际评选过程中,球员上赛季是否为MVP对于球员是否能选上MVP影响重大,投票者会有审美疲倦或更苛刻的要求,“连任MVP要求是更高的”。因此增加一个分类特征:上赛季是否MVP。
- NBA会分东西部赛区,且东西部的竞争格局有较大差异,“西强东弱”一直起来是一个趋势,投票者在投票时也会着重考虑东西部因素,因此增加一个分类特征:球员赛区。
- 处理完后的直接进模型的特征:
出场次数归一 | 首发率 | 出场时间 | 投篮命中率 | 命中个数 | 出手次数 |
三分命中率 | 三分命中个数 | 三分出手次数 | 罚球命中率 | 罚球命中个数 | 罚球出手次数 |
篮板数 | 前场篮板数 | 后场篮板数 | 助攻数 | 抢断数 | 盖帽数 |
失误数 | 犯规数 | 得分 | 出场胜率 | 上赛季是否MVP | 球员赛区 |
mvp得票率* |
(2)模型数据预处理:
-
有些中锋,或者出场时间较短的球员,其赛季数据中容易出现“命中率”类数据为缺失NA的情况,这种情况下根据实际意义可定义为0(例一个从未投出过三分球的球员,我们认为其三分命中率为0)。原始数据框中所有的NA数据都替换为0。
-
xgboost仅适用于数值型向量,因此在训练模型前需要对数据进行相应的转化预处理操作。另外,为了进一步提升运算效率,xgboost定义了独有的数据类型gb.DMatrix,方便对数据进行数值化和稀疏化处理。
mvpdata <- read.csv("mvpdata.csv")
mvpdata_13_17 <- mvpdata[which(mvpdata$season != "17-18" & mvpdata$season != "19-20"),] #13-14 ~ 16-17四个赛季为训练集
mvpdata_17_18 <- mvpdata[which(mvpdata$season == "17-18"),] #17-18赛季为测试集
train <- mvpdata_13_17[,5:29]
train[is.na(train)] <- 0 # NA替换为0
test <- mvpdata_17_18[,5:29]
test[is.na(test)] <- 0 # NA替换为0
library("xgboost")
library("Matrix")
train_matrix <- sparse.model.matrix(mvp_vote_rate ~ .-1, data = train)
test_matrix <- sparse.model.matrix(mvp_vote_rate ~ .-1, data = test)
train_label <- train[,25]
test_label <- test[,25]
train_final <- list(data=train_matrix,label=train_label)
test_final <- list(data=test_matrix,label=test_label)
dtrain <- xgb.DMatrix(data = train_final$data, label = train_final$label)
dtest <- xgb.DMatrix(data = test_final$data, label = test_final$label)
2.建模与评估
(1)训练模型及效果检验
- 利用13-14、14-15、15-16、16-17四个赛季的数据作为训练集,来预测17-18赛季的MVP(17-18赛季数据作为测试集)。
- 对球员MVP得票率影响最大的6个特征分别是:得分(53%)、抢断(12%)、命中数(10%)、出场胜率(9%)、助攻(5%)、出场时间(3%)。
Feature | Gain | Cover | Frequency | Importance |
scores | 0.534635654 | 0.675401362 | 0.16363636 | 0.534635654 |
steals | 0.119284767 | 0.020965539 | 0.16363636 | 0.119284767 |
shots_made | 0.100905008 | 0.237937718 | 0.03636364 | 0.100905008 |
wins_rate | 0.092871839 | 0.017824246 | 0.14545455 | 0.092871839 |
assists | 0.04828858 | 0.012322278 | 0.10909091 | 0.04828858 |
appearances_time | 0.025624747 | 0.004365649 | 0.03636364 | 0.025624747 |
- 本次模型的MAE=0.00117;模型预测得票率前十准确率为80%;模型预测的MVP为詹姆斯哈登,与实际情况一致。
- 采用交叉验证,即利用其中4年的数据预测另外一年的MVP得票率,5年的MVP预测全部准确!本部分代码省略。
xgb <- xgboost(data = dtrain,max_depth=6, eta=0.5, objective='reg:logistic', nround=25) #模型训练
importance <- xgb.importance(train_matrix@Dimnames[[2]], model = xgb)
head(importance)
xgb.ggplot.importance(importance) #各特征重要性贡献
pre_xgb = predict(xgb,newdata = dtest) #测试集效果检验
pre_xgb
mae <- sum(abs(pre_xgb-test_label))/length(test_label) #由于真实值含有大量的0值,因此用MAE来进行模型评估,MAE=0.00117
mvpdata_17_18["pre_xgb"] <- pre_xgb
head(mvpdata_17_18[order(-mvpdata_17_18$mvp_vote_rate),][c("player_name","mvp_vote_rate")],10) #实际得票前十
head(mvpdata_17_18[order(-mvpdata_17_18$pre_xgb),][c("player_name","pre_xgb")],10) #模型得票前十
得票率排名 | 实际情况 | 模型预测 | ||
球员 | 得票率 | 球员 | 得票率 | |
1 | 詹姆斯-哈登 | 36.75% | 詹姆斯-哈登 | 39.17% |
2 | 勒布朗-詹姆斯 | 28.10% | 勒布朗-詹姆斯 | 14.67% |
3 | 安东尼-戴维斯 | 16.95% | 斯蒂芬-库里 | 7.47% |
4 | 达米安-利拉德 | 7.88% | 安东尼-戴维斯 | 6.16% |
5 | 拉塞尔-威斯布鲁克 | 2.89% | 拉塞尔-威斯布鲁克 | 5.56% |
6 | 扬尼斯-阿德托昆博 | 2.86% | 凯文-杜兰特 | 3.47% |
7 | 凯文-杜兰特 | 2.51% | 扬尼斯-阿德托昆博 | 3.02% |
8 | 德玛尔-德罗赞 | 1.22% | 德马库斯-考辛斯 | 1.64% |
9 | 拉玛库斯-阿尔德里奇 | 0.23% | 凯里-欧文 | 1.38% |
10 | 斯蒂芬-库里 | 0.19% | 达米安-利拉德 | 0.52% |
(2)模型应用实际预测
- 利用13-14赛季~17-18赛季5个赛季的数据来预测19-20赛季的常规赛MVP。
- 本次预测结果2019-2020赛季常规赛MVP为詹姆斯-哈登,其得票率为18.96%;得票率前6分别为:詹姆斯-哈登、勒布朗-詹姆斯、安东尼-戴维斯、拉塞尔-维斯布鲁克、扬尼斯-阿德托昆博、科怀-伦纳德。
mvpdata_13_18 <- mvpdata[which(mvpdata$season != "19-20"),]
train2 <- mvpdata_13_18[,5:29]
train2[is.na(train2)] <- 0 # NA替换为0
train_matrix2 <- sparse.model.matrix(mvp_vote_rate ~ .-1, data = train2)
train_label2 <- train2[,25]
train_final2 <- list(data=train_matrix2,label=train_label2)
dtrain2 <- xgb.DMatrix(data = train_final2$data, label = train_final2$label) mvpdata_19_20 <- mvpdata[which(mvpdata$season == "19-20"),]
pred <- mvpdata_19_20[,5:29]
pred[is.na(pred)] <- 0 # NA替换为0
pred_matrix <- sparse.model.matrix(mvp_vote_rate ~ .-1, data = pred)
pred_label <- pred[,25]
pred_final <- list(data=pred_matrix,label=pred_label)
dpred <- xgb.DMatrix(data = pred_final$data, label = pred_final$label) xgb2 <- xgboost(data = dtrain2,max_depth=6, eta=0.5, objective='reg:logistic', nround=25)
pre_xgb2 = predict(xgb2,newdata = dpred)
mvpdata_19_20["pre_xgb2"] <- pre_xgb2
write.table(mvpdata_19_20,"mvppredresult.csv")
三、结论与评价
- 本次预测使用xgboost回归模型,通过数据预处理及特征工程建设,最终选取了关键的24个特征作为输入变量。模型效果由以下3点来说明:
(1)由于模型实际值0较多,因此选取MAE指标来进行评估,MAE=0.00117。
(2)采用交叉验证的方式,5个赛季的常规赛MVP均预测准确!
(3)得票率前十预测的准确率超过80%。
- 预测2019-2020年的常规赛MVP为詹姆斯-哈登,其得票率为18.96%,紧随其后的球员为勒布朗-詹姆斯、安东尼-戴维斯、拉塞尔-维斯布鲁克、扬尼斯-阿德托昆博、科怀-伦纳德。虽然我们有可能永远无法验证本次预测是否准确(NBA停摆,本赛季的MVP评选可能取消),但是至少这是对联盟500多位球员本赛季表现的一个客观评估。
- 最后说一下不足,本次预测选取了超级客观的24个特征,都是实实在在的赛场数据。但是在实际的评选过程中需要考虑的主观因素还有很多,如:球员的招黑程度/舆论风向,是否抱团组成三巨头,是否加入了一个已经具有夺冠实力的球队等;球员打球是否劲爆/飘逸,具有观赏性,狂造犯规骗罚球假摔大家都不喜欢,努力偏执场场绝杀肯定占优势;球员赛场外的表现,领导力、慈善等等。
- 完。
补充几行代码参考:
本文用的是一个回归任务,但是xgboost做分类的情况非常多,分类任务的评估(ROC、AUC):
pre_xgb = round(predict(xgb,newdata = dtest))
table(test_label,pre_xgb,dnn=c("true","pre"))#ROC曲线
xgboost_roc <- roc(test_label,as.numeric(pre_xgb))
plot(xgboost_roc, print.auc=TRUE, auc.polygon=TRUE,grid=c(0.1, 0.2),grid.col=c("green","red"),max.auc.polygon=TRUE,auc.polygon.col="skyblue",print.thres=TRUE,main='ROC curve')