Python實戰|用決策樹實現NBA獲勝球隊預測

NBA預測獲勝球隊

數據獲取

因爲疫情原因導致NBA2019-2020賽季沒有進行完,所以我們使用NBA2018-2019賽季的數據進行預測,數據獲取方式有下面兩種:

通過網站獲取

我們可以通過網站去獲取任意一年的所有場次比賽數據,獲取方式如下:

  1. 在瀏覽器中訪問https://www.basketball-reference.com/leagues/NBA_2019_games.html
  2. 點擊Share&more
  3. 點擊Get table as CSV (for Excel)
  4. 複製連帶表頭的所有數據到Excel的文本文件中
  5. 依次選擇每一個月份,複製不帶表頭的數據到上一個文件後

同時我們還需要獲取上賽季積分榜的數據,獲取方式如下:

  1. 在瀏覽器中訪問https://www.basketball-reference.com/leagues/NBA_2018_standings.html
  2. 點擊Expander Standings,可以看到整個賽季的戰況信息
  3. 點擊Export鏈接
  4. 複製數據並保存即可

通過GitHub獲取

本文中的數據和代碼已經上傳到GitHub上,鏈接如下:https://github.com/bigdatavalley/NBA_predict

數據準備

在進行預測的算法設計之前,我們需要先進行數據的查看和整合,由於原有數據中很多的特徵名稱設定的不是很好,所以我們在加載數據的時候重新設定一下列名稱,代碼如下:

import pandas as pd

path = 'basketball.csv'
dataset = pd.read_csv(path, parse_dates=['Date'])
dataset.columns = [
    'Date', 'Start(ET)', 'Visitor Team', 'VisitorPts', 'Home Team', 'HomePts',
    'OT?', 'Score Type', 'Notes'
]
dataset.head()

運行結果如下:

在這裏插入圖片描述

我們在建立預測模型的時候通常會有一個標準,當我們的預測效果超過這個標準的時候我們就可以認爲預測模型起到了作用,接下來我們來建立一個關於預測球隊獲勝的標準。

在NBA比賽中,通常分爲主客場,當球隊在主場比賽的時候往往會佔據一定的優勢,那麼如果我們每次都預測主場球隊獲勝,應該也能得到不錯的準確率,那麼我們就可以用這個數值來當作我們的標準,代碼如下:

# 提取新特徵
# 統計客隊得分小於主隊的場次
dataset['HomeWin'] = dataset['VisitorPts'] < dataset['HomePts']

# 保留成label
y_true = dataset['HomeWin'].values
# y_true
dataset['HomeWin'].mean()

運行結果如下:

0.5907012195121951

現在我們就有了一個0.59作爲標準,後面我們要保證建立的模型不低於這個準確率就可以了。

構建特徵

構造特徵一點一點進行,後面我們再陸續添加新的特徵。

進行模型建立之前,還有一個重要的過程就是構建我們要使用的特徵,第一個我們要思考的就是兩隻球隊是否贏得了上一場的比賽,通常我們會認爲贏得了上一場比賽的球隊就是更厲害的。我們建立兩個新的特徵分別是主隊/客隊是否贏了上一場比賽。代碼如下:

from collections import defaultdict

won_last = defaultdict(int)
dataset['HomeLastWin'] = 0
dataset['VisitorLastWin'] = 0

# 時間是無序的時候dataset.sort('Date').iterrows()
for index, row in dataset.iterrows():
    home_team = row['Home Team']
    visitor_team = row['Visitor Team']
    dataset.at[index, 'HomeLastWin'] = won_last[home_team]
    dataset.at[index, 'VisitorLastWin'] = won_last[visitor_team]
    won_last[home_team] = int(row['HomeWin'])
    won_last[visitor_team] = 1 - int(row['HomeWin'])

X_previouswins = dataset[['HomeLastWin', 'VisitorLastWin']].values
X_previouswins

運行結果如下:

array([[0, 0],
       [0, 0],
       [0, 0],
       ...,
       [0, 1],
       [1, 0],
       [1, 0]])

使用決策樹

我們嘗試使用簡單的決策樹算法進行簡單的訓練,並且通過交叉驗證的方式獲取評分。

決策樹的原理:
決策樹(上)
決策樹(下)
代碼如下:

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
import numpy as np

clf = DecisionTreeClassifier(random_state=42)

score = cross_val_score(clf, X_previouswins, y_true, scoring = 'accuracy')
print('score is:{0:.1f}%'.format(np.mean(score)*100))

運行結果如下:

score is:59.1%

根據結果可以發現,當前的特徵下,並沒有使得我們的預測效果提高,接下來我們繼續對數據和模型進行改進。

增加特徵

考慮到球隊獲勝可能和上個賽季的戰績也有一定的關係,我們嘗試加入上個賽季的數據來進行預測。

# 加入上賽季數據
standings = pd.read_excel('standings.xls')
# 建立新的特徵(排名情況)
dataset['HomeTeamRanksHigher'] = 0

for index, row in dataset.iterrows():
    home_team = row['Home Team']
    visitor_team = row['Visitor Team']
    home_rank = standings[standings['Team'] == home_team]['Rk'].values[0]
    visitor_rank = standings[standings['Team'] == visitor_team]['Rk'].values[0]
    row['HomeTeamRanksHigher'] = int(home_rank > visitor_rank)
    dataset.at[index, 'HomeTeamRanksHigher'] = int(home_rank < visitor_rank)
clf = DecisionTreeClassifier(random_state=42)
X_homehigher = dataset[[
    'HomeLastWin', 'VisitorLastWin', 'HomeTeamRanksHigher'
]].values

score = cross_val_score(clf, X_homehigher, y_true, scoring='accuracy')
print('score is:{0:.1f}%'.format(np.mean(score) * 100))

運行結果如下:

score is:62.2%

很明顯,我們加入上個賽季的戰績的時候,預測的準確率提升了3個百分點。

接下來我們繼續加入交手的兩隻球隊中哪一個在上一次交手中勝出的數據,代碼如下:

last_match_winner = defaultdict(int)
dataset['HomeTeamWonLast'] = 0
for index, row in dataset.iterrows():
    home_team = row['Home Team']
    visitor_team = row['Visitor Team']
    teams = tuple(sorted([home_team, visitor_team]))
    home_team_won_last = 1 if last_match_winner[teams] == row[
        'Home Team'] else 0
    dataset.at[index, 'HomeTeamWonLast'] = home_team_won_last
    winner = row['Home Team'] if row['HomeWin'] else row['Visitor Team']
    last_match_winner[teams] = winner
    
clf = DecisionTreeClassifier(random_state=42)
X_lastwinner = dataset[[
    'HomeLastWin', 'VisitorLastWin', 'HomeTeamRanksHigher', 'HomeTeamWonLast'
]].values

score = cross_val_score(clf, X_lastwinner, y_true, scoring='accuracy')
print('score is:{0:.1f}%'.format(np.mean(score) * 100))

運行結果如下:

score is:61.7%

結果和上面差異不大,沒有關係,我們繼續嘗試使用其他的策略來進行改進。

加入隊伍數據

我們之前做的工作都是根據一些比賽數據來進行的,下面我們嘗試加入隊伍數據來檢驗一下我們的運行成果,由於我們不能直接對字符數據進行計算,所以先把數據通過LabelEncoder轉化爲數值類型,但是一般數值類型又會有大小造成的影響,所以我們再進行一次OneHotEncoder的操作即可。代碼如下:

from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder

encoding = LabelEncoder()
onehot = OneHotEncoder()

encoding.fit(dataset['Home Team'].values)
home_teams = encoding.transform(dataset['Home Team'].values)
visitor_teams = encoding.transform(dataset['Visitor Team'].values)
X_teams = np.vstack([home_teams, visitor_teams]).T
X_teams = onehot.fit_transform(X_teams).todense()
X_all = np.hstack([X_lastwinner, X_teams])
clf = DecisionTreeClassifier(random_state=42)
score = cross_val_score(clf, X_all, y_true, scoring='accuracy')
print('score is:{0:.1f}%'.format(np.mean(score) * 100))

運行結果如下:

score is:60.7%

很顯然雖然比我們直接預測主隊獲勝的效果要好,但是也沒有高出很多。不要慌,我們繼續使用更好的算法來嘗試一下。

使用隨機森林

隨機森林是一種以決策樹爲基分類器的集成學習算法,理論上講,我們所得到的效果要好於決策樹。

代碼如下:

clf = RandomForestClassifier(random_state=42)
score = cross_val_score(clf, X_all, y_true, scoring='accuracy')
print('score is:{0:.1f}%'.format(np.mean(score) * 100))

運行結果如下:

score is:63.6%

嘗試使用網格搜索,代碼如下:

from sklearn.model_selection import GridSearchCV

parameter_space = {
    'max_features': [2, 10, 'auto'],
    'n_estimators': [100, 200],
    'criterion': ['gini', 'entropy'],
    'min_samples_leaf': [2, 4, 6]
}
clf = RandomForestClassifier(random_state=42)
grid = GridSearchCV(clf, parameter_space)
grid.fit(X_all, y_true)
print('score is:{0:.1f}%'.format(np.mean(score) * 100))

運行結果如下:

score is:63.6%

可以看到根據目前我們所構造的幾個簡單的特徵就已經可以得到顯著的準確率提升了。同時收到球員轉會、受傷等情況的影響,對於不同賽季的數據使用這些方法會得到不同的結果,有興趣的讀者可以自己獲取其他賽季的數據來嘗試一下。

進一步考慮

你可能對當前的結果還不是很滿意,可以嘗試進行下面幾個方向的參考:

  • 上一次比賽距離本次比賽的時間
  • 最近半個月的比賽狀態
  • 雙方在最近幾場比賽的戰績
  • 每個球隊在客場和主場的表現情況
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章