天天看點

機器學習實戰 利用sklearn庫預測科比生涯資料

一、前言

本文所講的實戰内容是用機器學習的方法來分析科比職業生涯資料的執行個體,利用随機森林算法訓練處一個預測科比投籃模型。主要用了python的numpy,pandas,matplotlib和sklearn庫。

本文出現的所有代碼,均可在我的github上下載下傳,歡迎Follow、Star:github位址

二、設計思路

先來看看這份科比生涯的資料集:資料集下載下傳

機器學習實戰 利用sklearn庫預測科比生涯資料
機器學習實戰 利用sklearn庫預測科比生涯資料

這個表格記錄了科比30000多個鏡頭的詳細資料,共有25個标簽。

具體的設計思路是将這25個标簽代表的資料進行分析,找出對科比投籃結果有影響的标簽,利用機器學習中随機森林的算法訓練出可以預測科比是否能夠投籃命中的模型。

先來看看這25個标簽具體代表什麼(自己不是籃球的專業人士和愛好者,是以具體的内容可能有所出入,不過不會影響到分析結果)

  • action_type(用什麼方式投的籃)
  • combined_shot_type(結合什麼方式投籃)
  • game_event_id(遊戲事件ID)
  • game_id(遊戲ID)
  • la(投籃的經度)
  • loc_x (投籃的x坐标)
  • loc_y(投籃的y坐标)
  • lon(投籃的緯度)
  • minutes_remaining(離比賽結束還有多少分鐘)
  • period(第幾場)
  • playoffs(是不是季後賽)
  • season(賽季)
  • seconds_remaining(離比賽結束還有多少秒)
  • shot_distance(投籃離籃筐的的距離)
  • shot_made_flag (是不是進球了(主要的标簽))
  • shot_type(2分球還是3分球區域)
  • shot_zone_area(投籃區域的表示方法一)
  • shot_zone_basic(投籃區域的表示方法二)
  • shot_zone_range(投籃區域的表示方法三)
  • team_id(隊伍ID)
  • team_name(隊伍名字)
  • game_date(比賽時間)
  • matchup(比賽雙方隊伍)
  • opponent(自己所在隊伍名字)
  • shot_id(鏡頭ID)

可以看到,這25個标簽中對于科比能否投籃命中有一些無關緊要的資料,比如team_id,因為這30000多份樣本中全是在湖人隊打的,shot_id,game_id等等這個資料也無關緊要,具體的分析将會在下面講解。

三、資料分析

首先我們導入資料,編寫代碼如下

import pandas as pd

# 導入資料
filename= "data.csv"
raw = pd.read_csv(filename)
print(raw.shape)
print(raw.head())  #head函數列印前5行,如果需要列印前10行,這樣寫
           

運作結果如下

機器學習實戰 利用sklearn庫預測科比生涯資料

接下來我們再來分析這一份資料表,我們發現其中shot_made_flag這個标簽竟然有缺失值,這個表示了科比是否進球了,作為最重要的資料,是不能随意進行填充的,我們必須删除掉這些樣本進行下一步的工作,代碼如下

import pandas as pd

# 導入資料
filename= "data.csv"
raw = pd.read_csv(filename)

kobe =  raw[pd.notnull(raw['shot_made_flag'])]
print(kobe.shape)
           

運作結果如下

機器學習實戰 利用sklearn庫預測科比生涯資料

此時我們隻有25697個資料進行訓練了。

接着我們分析lat,loc_x,loc_y,lon這4個标簽,這4個标簽說明了科比投籃的位置,而具體指的是什麼呢,有什麼關系嗎,我們畫散點圖來看一下。

編寫代碼如下

import pandas as pd
import matplotlib.pyplot as plt

# 導入資料
filename= "data.csv"
raw = pd.read_csv(filename)


#删除shot_made_flag為空的資料項,并且命名為kobe用作訓練
kobe =  raw[pd.notnull(raw['shot_made_flag'])]



#畫散點圖用來分析lat loc_x  loc_y lon這4個标簽
alpha =    #指定一個數字,用于後面的透明度
plt.figure(figsize=(,))  #指定畫圖域
# loc_x and loc_y
plt.subplot()    #一行兩列   第一個位置
plt.scatter(kobe.loc_x, kobe.loc_y, color='R', alpha=alpha)   #畫散點圖
plt.title('loc_x and loc_y')
# lat and lon
plt.subplot()    #一行兩列   第二個位置
plt.scatter(kobe.lon, kobe.lat, color='B', alpha=alpha)
plt.title('lat and lon')
plt.show()
           

運作結果如圖所示

機器學習實戰 利用sklearn庫預測科比生涯資料

我們大緻可以看出,這4個坐标大緻表示了距離籃筐的距離,那樣的話,我們接下來用于資料處理的時候選擇其中的一組資料即可了。

shot_type,shot_zone_area,shot_zone_basic,shot_zone_range 這4個标簽代表了投籃的區域,其實還是說明一件事,這裡就不做贅述了,當然shot_zone_area,shot_zone_basic,shot_zone_range這3個标簽将投籃區域相比于shot_type來說分的更細,直接删掉是不是會有問題,其實大可不必擔心,因為,接下來我們将會用極坐标的形式表示科比的投籃位置,将更會細化科比的投籃區域。

四、資料處理

首先處理我們上節所說的極坐标的問題,然後我們會發現算出來的dist和,shot_distance竟然具有正相關性。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time
from sklearn.ensemble import RandomForestClassifier  #導入随機森林分類器
from sklearn.cross_validation import KFold
from sklearn.metrics import log_loss

# 導入資料
filename= "data.csv"
raw = pd.read_csv(filename)


#删除shot_made_flag為空的資料項,并且命名為kobe用作訓練
kobe =  raw[pd.notnull(raw['shot_made_flag'])]


#對于lat,loc_x,loc_y,lon這4個标簽,我們取loc_x和loc_y這2個标簽,并将其轉化為極坐标的形式
#dist表示離籃筐的距離,angle表示投籃的角度,這樣将會更好的科比投籃的反應結果`
raw['dist'] = np.sqrt(raw['loc_x']** + raw['loc_y']**)
loc_x_zero = raw['loc_x'] == 
raw['angle'] = np.array([]*len(raw))
raw['angle'][~loc_x_zero] = np.arctan(raw['loc_y'][~loc_x_zero] / raw['loc_x'][~loc_x_zero])
raw['angle'][loc_x_zero] = np.pi / 

#畫圖展示dist和shot_distance的正相關性
plt.figure(figsize=(,))
plt.scatter(raw.dist, raw.shot_distance, color='blue')
plt.title('dist and shot_distance')
plt.show()
           

運作結果如下

機器學習實戰 利用sklearn庫預測科比生涯資料

這樣我們可以保留其中的一個(這裡我們保留了dist這個标簽),接着我們将minutes_remaining和seconds_remaining轉化成一個标簽remaining_time,然後删除不必要的列,非數值型的轉換成onehot編碼格式

具體編寫代碼如下,具體說明在代碼注釋中

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 導入資料
filename= "data.csv"
raw = pd.read_csv(filename)


#删除shot_made_flag為空的資料項,并且命名為kobe用作訓練
kobe =  raw[pd.notnull(raw['shot_made_flag'])]



#對于lat,loc_x,loc_y,lon這4個标簽,我們取loc_x和loc_y這2個标簽,并将其轉化為極坐标的形式
#dist表示離籃筐的距離,angle表示投籃的角度,這樣将會更好的科比投籃的反應結果
raw['dist'] = np.sqrt(raw['loc_x']** + raw['loc_y']**)
loc_x_zero = raw['loc_x'] == 
raw['angle'] = np.array([]*len(raw))
raw['angle'][~loc_x_zero] = np.arctan(raw['loc_y'][~loc_x_zero] / raw['loc_x'][~loc_x_zero])
raw['angle'][loc_x_zero] = np.pi / 



#對于minutes_remaining:離比賽結束還有多少分鐘;seconds_remaining:離比賽結束還有多少秒(0-60),這
#2個屬性我們合成距離比賽結束的時間
raw['remaining_time'] = raw['minutes_remaining'] *  + raw['seconds_remaining']


#機器學習隻能識别數值型的資料
#将賽季中'Jan-00' 'Feb-01' 'Mar-02'  ···  '1998-99'轉換成
# 0  1  2  ··· 99
raw['season'] = raw['season'].apply(lambda x: int(x.split('-')[]) )


#删除對于比賽結果沒有影響的資料
drops = ['shot_id', 'team_id', 'team_name', 'shot_zone_area', 'shot_zone_range', 'shot_zone_basic','matchup', 'lon',
         'lat', 'seconds_remaining', 'minutes_remaining','shot_distance', 'loc_x', 'loc_y', 'game_event_id', 'game_id',
         'game_date']
for drop in drops:
    raw = raw.drop(drop, )


#将非數值型的資料轉換成為onehot編碼的格式,加入到資料中并且将原來的資料删除
categorical_vars = ['action_type', 'combined_shot_type', 'shot_type', 'opponent', 'period', 'season']
for var in categorical_vars:
    raw = pd.concat([raw, pd.get_dummies(raw[var], prefix=var)], )
    raw = raw.drop(var, )
print(raw)
           

結果調試器裡面顯示的效果不太好,隻附上最後的行和列吧

機器學習實戰 利用sklearn庫預測科比生涯資料

為什麼會有129行之多,是因為我們用了onehot編碼,具體什麼是onehot編碼這裡就不做贅述了,感興趣的可以谷歌或者百度一下。

最後我們總結一下,到底這25個标簽還剩下什麼,首先除去和比賽結果無關的标簽,’shot_id’, ‘team_id’, ‘team_name’, ‘shot_zone_area’, ‘shot_zone_range’, ‘shot_zone_basic’,’matchup’, ‘lon’,

‘lat’, ‘seconds_remaining’, ‘minutes_remaining’,’shot_distance’, , ‘game_event_id’, ‘game_id’,

‘game_date’

然後’loc_x’, ‘loc_y’轉換成了極坐标的形式,變成了’dist’,’angle’;’seconds_remaining’和’minutes_remaining’合并成了’remaining_time’。

最後将’action_type’, ‘combined_shot_type’, ‘shot_type’, ‘opponent’, ‘period’, ‘season’轉換成onehot編碼格式。

至此我們的資料處理工作基本完成了。

五、利用sklearn來進行資料的處理

具體的思路是利用随機森林分類器配合着交叉驗證的方法進行資料的分析,先找到最佳的樹的個數,和樹的深度。

編寫代碼如下

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time
from sklearn.ensemble import RandomForestClassifier  #導入随機森林分類器
from sklearn.cross_validation import KFold
from sklearn.metrics import log_loss

# 導入資料
filename= "data.csv"
raw = pd.read_csv(filename)


#删除shot_made_flag為空的資料項,并且命名為kobe用作訓練
kobe =  raw[pd.notnull(raw['shot_made_flag'])]



#對于lat,loc_x,loc_y,lon這4個标簽,我們取loc_x和loc_y這2個标簽,并将其轉化為極坐标的形式
#dist表示離籃筐的距離,angle表示投籃的角度,這樣将會更好的科比投籃的反應結果`
raw['dist'] = np.sqrt(raw['loc_x']** + raw['loc_y']**)
loc_x_zero = raw['loc_x'] == 
raw['angle'] = np.array([]*len(raw))
raw['angle'][~loc_x_zero] = np.arctan(raw['loc_y'][~loc_x_zero] / raw['loc_x'][~loc_x_zero])
raw['angle'][loc_x_zero] = np.pi / 



# 對于minutes_remaining:離比賽結束還有多少分鐘;seconds_remaining:離比賽結束還有多少秒(0-60),這
# 2個屬性我們合成距離比賽結束的時間
raw['remaining_time'] = raw['minutes_remaining'] *  + raw['seconds_remaining']


#機器學習隻能識别數值型的資料
#将賽季中'Jan-00' 'Feb-01' 'Mar-02'  ···  '1998-99'轉換成
# 0  1  2  ··· 99
raw['season'] = raw['season'].apply(lambda x: int(x.split('-')[]) )


# 删除對于比賽結果沒有影響的資料
drops = ['shot_id', 'team_id', 'team_name', 'shot_zone_area', 'shot_zone_range', 'shot_zone_basic','matchup', 'lon',
         'lat', 'seconds_remaining', 'minutes_remaining','shot_distance', 'loc_x', 'loc_y', 'game_event_id', 'game_id',
         'game_date']
for drop in drops:
    raw = raw.drop(drop, )


#将非數值型的資料轉換成為onehot編碼的格式,加入到資料中并且将原來的資料删除
categorical_vars = ['action_type', 'combined_shot_type', 'shot_type', 'opponent', 'period', 'season']
for var in categorical_vars:
    raw = pd.concat([raw, pd.get_dummies(raw[var], prefix=var)], )
    raw = raw.drop(var, )



#将資料分為訓練集和測試集
train_kobe = raw[pd.notnull(raw['shot_made_flag'])]
train_label = train_kobe['shot_made_flag']
train_kobe = train_kobe.drop('shot_made_flag', )

test_kobe = raw[pd.isnull(raw['shot_made_flag'])]
test_kobe = test_kobe.drop('shot_made_flag', )



print('尋找随機森林分類器的的最佳樹的數量...')
min_score = 
best_n = 
scores_n = []
range_n = np.logspace(, , num=).astype(int)
for n in range_n:
    print('樹的數量 : {0}'.format(n))
    t1 = time.time()
    rfc_score = 
    rfc = RandomForestClassifier(n_estimators=n)
    for train_k, test_k in KFold(len(train_kobe), n_folds=, shuffle=True):
        rfc.fit(train_kobe.iloc[train_k], train_label.iloc[train_k])
        pred = rfc.predict(train_kobe.iloc[test_k])
        rfc_score += log_loss(train_label.iloc[test_k], pred) / 
    scores_n.append(rfc_score)
    if rfc_score < min_score:
        min_score = rfc_score
        best_n = n
    t2 = time.time()
    print('建造 {0} 顆樹(耗時{1:.3f}秒)'.format(n, t2 - t1))
print("最佳樹的顆樹為 : {0},得分為: {1}".format(best_n,min_score))

print('\n')

print('尋找随機森林分類器的最佳樹的最佳深度...')
min_score = 
best_m = 
scores_m = []
range_m = np.logspace(, , num=).astype(int)
for m in range_m:
    print("樹的最大的深度 : {0}".format(m))
    t1 = time.time()
    rfc_score = 
    rfc = RandomForestClassifier(max_depth=m, n_estimators=best_n)
    for train_k, test_k in KFold(len(train_kobe), n_folds=, shuffle=True):
        rfc.fit(train_kobe.iloc[train_k], train_label.iloc[train_k])
        pred = rfc.predict(train_kobe.iloc[test_k])
        rfc_score += log_loss(train_label.iloc[test_k], pred) / 
    scores_m.append(rfc_score)
    if rfc_score < min_score:
        min_score = rfc_score
        best_m = m
    t2 = time.time()
    print('樹的最大深度為: {0}(耗時{1:.3f}秒)'.format(m, t2 - t1))
print('最佳樹的深度: {0},得分為:{1}'.format(best_m, min_score))

plt.figure(figsize=(,))
plt.subplot()
plt.plot(range_n, scores_n)
plt.ylabel('score')
plt.xlabel('number of trees')

plt.subplot()
plt.plot(range_m, scores_m)
plt.ylabel('score')
plt.xlabel('max depth')
plt.show()
           

運作結果如下,說明當樹的顆樹為100,并且深度為12的時候,損失函數最小,下面是具體的得分圖示。

機器學習實戰 利用sklearn庫預測科比生涯資料

下面我們用100,12這個參數訓練模型,并且預測出5000個’shot_made_flag’的缺失值。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time
from sklearn.ensemble import RandomForestClassifier  #導入随機森林分類器
from sklearn.cross_validation import KFold
from sklearn.metrics import log_loss

# 導入資料
filename = "data.csv"
raw = pd.read_csv(filename)


# 删除shot_made_flag為空的資料項,并且命名為kobe用作訓練
kobe = raw[pd.notnull(raw['shot_made_flag'])]


# 對于lat,loc_x,loc_y,lon這4個标簽,我們取loc_x和loc_y這2個标簽,并将其轉化為極坐标的形式
# dist表示離籃筐的距離,angle表示投籃的角度,這樣将會更好的科比投籃的反應結果`
raw['dist'] = np.sqrt(raw['loc_x']** + raw['loc_y']**)
loc_x_zero = raw['loc_x'] == 
raw['angle'] = np.array([]*len(raw))
raw['angle'][~loc_x_zero] = np.arctan(raw['loc_y'][~loc_x_zero] / raw['loc_x'][~loc_x_zero])
raw['angle'][loc_x_zero] = np.pi / 


# 對于minutes_remaining:離比賽結束還有多少分鐘;seconds_remaining:離比賽結束還有多少秒(0-60),這
# 2個屬性我們合成距離比賽結束的時間
raw['remaining_time'] = raw['minutes_remaining'] *  + raw['seconds_remaining']


# 機器學習隻能識别數值型的資料
# 将賽季中'Jan-00' 'Feb-01' 'Mar-02'  ···  '1998-99'轉換成
# 0  1  2  ··· 99
raw['season'] = raw['season'].apply(lambda x: int(x.split('-')[]) )



#删除對于比賽結果沒有影響的資料
drops = ['shot_id', 'team_id', 'team_name', 'shot_zone_area', 'shot_zone_range', 'shot_zone_basic','matchup', 'lon',
         'lat', 'seconds_remaining', 'minutes_remaining','shot_distance', 'loc_x', 'loc_y', 'game_event_id', 'game_id',
         'game_date']
for drop in drops:
    raw = raw.drop(drop, )


#将非數值型的資料轉換成為onehot編碼的格式,加入到資料中并且将原來的資料删除
categorical_vars = ['action_type', 'combined_shot_type', 'shot_type', 'opponent', 'period', 'season']
for var in categorical_vars:
    raw = pd.concat([raw, pd.get_dummies(raw[var], prefix=var)], )
    raw = raw.drop(var, )
# print(raw)


# 将資料分為訓練集和測試集
train_kobe = raw[pd.notnull(raw['shot_made_flag'])]
train_label = train_kobe['shot_made_flag']
train_kobe = train_kobe.drop('shot_made_flag', )

test_kobe = raw[pd.isnull(raw['shot_made_flag'])]
test_kobe = test_kobe.drop('shot_made_flag', )


# 訓練模型并且用預測shot_made_flag的缺失值
model = RandomForestClassifier(n_estimators=, max_depth=)
model.fit(train_kobe, train_label)
predictions = model.predict(test_kobe)
result=pd.DataFrame({'shot_id':test_shot_id['shot_id'].as_matrix(),'shot_made_flag':predictions.astype(np.int32)})
result.to_csv("result.csv", index=False)
           

運作結果如下圖

機器學習實戰 利用sklearn庫預測科比生涯資料

這裡給出了5000個缺失值。

六、總結

本篇文章主要用了機器學習的sklearn庫,配合着numpy,pandas,matplotlib的技術路線,利用随機森林算法對科比生涯資料進行分析,對缺失值進行了預測。