科比职业生涯回顾与模型预测
2016年4月13日,NBA巨星科比退役,结束了自己的篮球生涯。2020年1月26日,科比去世,令球迷悲痛不已。在此回顾科比20年的NBA生涯,以更深入的了解这位传奇,同时向科比致敬。
本文主要内容如下:
- 数据采集
- 数据清洗
- 数据分析
- 数据概览
- 胜率
- 出勤率
- 命中率
- 对阵球队
- 时间序列模型预测
- 比赛结果预测
- 结论
一、数据采集
import pandas as pd
# 爬取科比常规赛数据
df = pd.read_html('http://www.stat-nba.com/query.php?page=0&QueryType=game&GameType=season&Player_id=195&crtcol=season&order=1#label_show_result',encoding='utf-8',index_col=0)[0]
for page in range(67):
url = f'http://www.stat-nba.com/query.php?page={page+1}&QueryType=game&GameType=season&Player_id=195&crtcol=season&order=1#label_show_result'
temp = pd.read_html(url,encoding='utf-8',index_col=0)[0]
df = pd.concat([df,temp])
df.to_csv('kobe_regular.csv',index=None)
# 爬取科比季后赛数据
df_2 = pd.read_html('http://www.stat-nba.com/query.php?page=0&QueryType=game&GameType=playoff&Player_id=195&crtcol=season&order=1#label_show_result',encoding='utf-8',index_col=0)[0]
for page in range(10):
url = f'http://www.stat-nba.com/query.php?page={page+1}&QueryType=game&GameType=playoff&Player_id=195&crtcol=season&order=1#label_show_result'
temp = pd.read_html(url,encoding='utf-8',index_col=0)[0]
df_2 = pd.concat([df_2,temp])
df_2.to_csv('kobe_playoff.csv',index=None)
二、数据清洗
1、缺失值处理及数据替换
# 读取数据
import pandas as pd
df_r = pd.read_csv('kobe_regular.csv')
df_o = pd.read_csv('kobe_playoff.csv')
# 替换"结果"中的字符
df_r.replace({
'胜':1,'负':0},inplace=True)
df_o.replace({
'胜':1,'负':0},inplace=True)
# 将字符串转换为float
df_r[['投篮','三分','罚球']]=df_r[['投篮','三分','罚球']].apply(lambda x:x.str.replace('%', '').astype('float')/100)
df_o[['投篮','三分','罚球']]=df_o[['投篮','三分','罚球']].apply(lambda x:x.str.replace('%', '').astype('float')/100)
# def convert_percent(x):
# new = x.str.replace('%','')
# return float(new)/100
# df['投篮'].apply(convert_percent)
#查看缺失数据
df_r.isnull().values.sum()
df_o.isnull().values.sum()
25
df_r[df_r.isnull().any(axis=1)].iloc[:,7:].head(10)
命中 | 出手 | 三分 | 命中.1 | 出手.1 | 罚球 | 命中.2 | 出手.2 | 篮板 | 前场 | 后场 | 助攻 | 抢断 | 盖帽 | 失误 | 犯规 | 得分 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
7 | 1 | 7 | 0.000 | 0 | 2 | NaN | 0 | 0 | 2 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 2 |
27 | 4 | 13 | 0.286 | 2 | 7 | NaN | 0 | 0 | 4 | 0 | 4 | 3 | 2 | 0 | 2 | 2 | 10 |
28 | 5 | 9 | 0.000 | 0 | 1 | NaN | 0 | 0 | 2 | 0 | 2 | 1 | 0 | 0 | 5 | 1 | 10 |
29 | 2 | 9 | 0.250 | 1 | 4 | NaN | 0 | 0 | 2 | 1 | 1 | 6 | 0 | 1 | 3 | 2 | 5 |
31 | 2 | 5 | 0.500 | 1 | 2 | NaN | 0 | 0 | 2 | 0 | 2 | 9 | 1 | 0 | 4 | 0 | 5 |
33 | 4 | 15 | 0.000 | 0 | 4 | NaN | 0 | 0 | 6 | 2 | 4 | 3 | 2 | 1 | 2 | 2 | 8 |
47 | 5 | 13 | 0.500 | 1 | 2 | NaN | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 2 | 2 | 11 |
49 | 2 | 15 | 0.167 | 1 | 6 | NaN | 0 | 0 | 3 | 0 | 3 | 1 | 1 | 0 | 3 | 2 | 5 |
57 | 5 | 13 | 0.000 | 0 | 5 | NaN | 0 | 0 | 4 | 0 | 4 | 5 | 2 | 0 | 3 | 2 | 10 |
69 | 2 | 12 | 0.000 | 0 | 3 | NaN | 0 | 0 | 8 | 0 | 8 | 7 | 1 | 0 | 6 | 1 | 4 |
缺失值均为命中率,且仅在无出手时才为0,理应删除该类缺失,但此处先保留,分析时再做处理
2、将“比赛”列拆分为对阵双方的比分,方便后续比较
import re
# 将“比赛”列拆分为对阵双方
df_r_score= df_r['比赛'].str.split('-',1,expand=True).rename(columns={
0:'对手',1:'湖人'})
df_o_score= df_o['比赛'].str.split('-',1,expand=True).rename(columns={
0:'对手',1:'湖人'})
# 提取湖人比分
df_r_score['湖人得分']=df_r_score['湖人'].str.extract(r'(\d+)',expand=True)
df_o_score['湖人得分']=df_o_score['湖人'].str.extract(r'(\d+)',expand=True)
# 将对手比分和队名拆分,其中队名中“76人”既有数字又有字符,因此使用正则表达式
rival = []
for i in range(len(df_r)):
tem = re.split(r'(\d+\D|\D+)',df_r_score['对手'][i],maxsplit=1)
rival.append(tem)
# 合并两队数据,删除无用列
df_r_score = df_r_score.join(pd.DataFrame(rival)).drop(['对手','湖人',0],axis=1).rename(columns={
1:'对手',2:'对手得分'