一、簡介
1壳繁、數(shù)據(jù)獲取
我是直接在這里下載數(shù)據(jù),這個網(wǎng)站還有類型的數(shù)據(jù),總體還是比較全面的甩十。
2、數(shù)據(jù)介紹
這個數(shù)據(jù)集收集近一萬場鉆石一到大師的比賽游戲前十分鐘的數(shù)據(jù)吭产。一共有四十列屬性侣监。下面主要介紹一下每個屬性的含義。
標簽 | 含義 |
---|---|
gameId | 游戲ID |
blueWins | 藍方是否獲勝(同時也是數(shù)據(jù)的目標值) |
blueWardsPlaced | 藍色方眼數(shù) |
blueWardsDestroyed | 藍色方被排眼數(shù) |
blueFirstBlood | 藍色方是否獲得第一滴血 |
blueKills | 藍色方擊殺數(shù) |
blueDeaths | 藍色方死亡數(shù) |
blueAssists | 藍色方的助攻數(shù) |
blueEliteMonsters | 藍色方獲取的小龍數(shù) |
blueDragons | 藍色方大龍數(shù) |
blueHeralds | 藍色方先鋒數(shù) |
blueTowersDestroyed | 藍色方被摧毀的防御塔 |
blueTotalGold | 藍色方的總經(jīng)濟 |
blueAvgLevel | 藍色方平均等級 |
blueTotalExperience | 藍色方的總經(jīng)驗 |
blueTotalMinionsKilled | 藍色方補兵數(shù) |
blueTotalJungleMinionsKilled | 藍色方擊殺野怪 |
blueGoldDiff | 雙方經(jīng)濟差 |
blueExperienceDiff | 經(jīng)驗差 |
blueCSPerMin | 藍色方最少個人補兵數(shù) |
blueGoldPerMin | 藍色方個人最少經(jīng)濟 |
后面的紅色方同理臣淤。
3橄霉、數(shù)據(jù)簡要分析
首先由于數(shù)據(jù)為游戲前十分鐘數(shù)據(jù),而實際上游戲是20分鐘才刷新大龍邑蒋,因此上面的大龍擊殺數(shù)便不需要姓蜂。
除此之外會發(fā)現(xiàn)雙方插眼數(shù)有些明顯大于實際情況,初始飾品每支2分鐘cd寺董,每個人有一只如果一開始就插眼那么十分鐘團隊最多插20只飾品眼覆糟,但是由于實際情況中隊伍還會購買真眼等,所以我預估每隊十分鐘最多插28只眼左右遮咖。因此大于該值的數(shù)據(jù)都是偏離實際的滩字。
二、數(shù)據(jù)處理及模型建立
1、整體預覽
首先是導入庫
#導入庫
import pandas as pd
import numpy as np
讀取下載好的數(shù)據(jù)
df = pd.read_csv('high_diamond_ranked_10min.csv')
print(df.head())
整體查看數(shù)據(jù)
#整體查看數(shù)據(jù)
df.info()
這里可以看到數(shù)據(jù)類型以及是否有空值麦箍,當然空值也可以通過df.isnull().sum()來查看漓藕,這一語句是專門查看空值的。
通過對藍色方勝利值的統(tǒng)計挟裂,可以知道該數(shù)據(jù)是否獲勝場次是相近的享钞,說明數(shù)據(jù)也是很平衡的。
2诀蓉、處理異常值
剛剛上面在數(shù)據(jù)的簡單介紹中提到了插眼數(shù)是有些偏離實際情況的栗竖,所以我畫了個箱線圖來查看。
可以發(fā)現(xiàn)異常值其實非常多渠啤,并且偏離也很大狐肢,由于值太多近兩千個所以并不能將其刪除只能將其進行替換。
#對超過實際情況的值進行替換沥曹,十分鐘最多眼數(shù)也就28左右份名,那些超過的應該是一整場的數(shù)據(jù)了
def get_WardsPlaced(data,name):
print(len(df[data> 22])) #有1978行數(shù)據(jù)偏離事實
x_bar = data.mean() #均值
x_std = data.std() #標準差
#統(tǒng)計量
print("未處理前的統(tǒng)計量:",data.describe())
p75 = data.quantile(q=0.75)
df[name] = data
df.loc[data > 28,name] = p75
print("進行值替換后再查看其統(tǒng)計量:",df[name].describe())
get_WardsPlaced(df['blueWardsPlaced'],'blueWardsPlaced_new')
這里對藍色方的插眼數(shù)進行替換,替換完的箱線圖如下:
值就相對集中也不會出現(xiàn)非常多的偏離值了妓美。
處理完插眼數(shù)之后會發(fā)現(xiàn)排眼數(shù)有些甚至是多于插眼數(shù)的僵腺,這種就是數(shù)據(jù)錯誤了,有些是因為我修改了插眼數(shù)的值壶栋,有些是本來就大于原插眼數(shù)的辰如,于是全部都進行刪除。
#有38行數(shù)據(jù)不符合實際要求贵试,被排眼數(shù)大于插眼數(shù)
print(len(df[df['blueWardsDestroyed'] > df['blueWardsPlaced_new']]))
df.drop(index=df[df['blueWardsDestroyed'] > df['blueWardsPlaced_new']].index,axis=1,inplace=True)
3丧没、可視化
通過這幅散點圖可以明顯的看出當某一方在十分鐘經(jīng)濟差距超過(-6324,6744)這一區(qū)間的話,落后一方便沒有翻盤的可能性锡移。因為英雄聯(lián)盟主要就是通過對線打團以及資源的把控來獲得經(jīng)濟呕童,將經(jīng)濟轉(zhuǎn)換為裝備領(lǐng)先,最終戰(zhàn)勝對方來推倒防御塔淆珊,所以經(jīng)濟對比賽結(jié)果影響非常大夺饲。
上圖是經(jīng)驗的差距,經(jīng)驗主要體現(xiàn)于等級的領(lǐng)先施符,如果雙方對線等級相差兩級以上往声,那么低等級的一方會非常弱勢,所以我除了畫經(jīng)驗的差距圖之外同時畫出雙方平均等級的散點圖戳吝。
通過該散點圖我們可以明顯的看到當藍色方的平均等級處于6.4而紅色方在6.0以上那么浩销,藍色方基本沒有獲勝的希望,當然這只是從數(shù)據(jù)中得到的信息听哭,并不代表全部比賽慢洋,因為雙方都處于6點幾的等級時還是勝負難分塘雳,這需要看其他的影響因素。
當藍色方平均等級在7.0而紅色方在6.0以下那么紅色方便沒有獲勝的希望普筹。這個拓展到全部比賽還是比較有信服力度的败明,因為平均隊伍每個人領(lǐng)先對面一個等級以上。
通過這幅圖我們可以看到當紅色方擊殺9個人以上而藍色方擊殺在9個人以下太防,那么大概率是紅色方獲勝妻顶。而反過來也同樣成立。
在游戲中視野對于比賽的影響也非常大蜒车,但是從這幅圖中并沒有很好的反應出來讳嘱,我覺得主要原因是插眼數(shù)減去被排眼數(shù)并不能很好的反應出視野的得分,因為一只眼可能插在那里并沒有發(fā)揮什么作用酿愧,比如在自己家野區(qū)插了個真眼但是前十分鐘對方只顧著刷自己野區(qū)的野和抓人并沒有來反野呢燥,那么這個眼其實就是沒有發(fā)揮什么作用的,所以感覺還是數(shù)據(jù)的問題寓娩,但是由于沒有更好的數(shù)據(jù)替代只能將就著了。
3呼渣、生成特征
我主要用了13個特征
特征 | 介紹 |
---|---|
blue_view | 藍色方的視野(插眼減去被排的眼) |
blueTowersDestroyed | 藍色方摧毀的防御塔(十分鐘獲得一塔說明游戲優(yōu)勢很大) |
blueKDA | 擊殺助攻以及死亡數(shù)計算而出 |
blue_num_pkill | 藍色方每次擊殺由幾個人完成(通過助攻加擊殺的和除以擊殺棘伴,體現(xiàn)支援的速度) |
blueEliteMonsters | 小龍數(shù)(由于現(xiàn)在是龍魂版本所以小龍也很重要) |
blueGoldDiff | 雙方的經(jīng)濟差 |
grade_Diff | 雙方平均等級差 |
紅色方同理。
生成這些特征后屁置,由于有除法所以數(shù)據(jù)有一些nan值和無窮大值存在焊夸,所以又要對數(shù)據(jù)進行清洗一下。
4蓝角、建立模型
我使用的是knn近鄰分類算法阱穗,將數(shù)據(jù)劃分為訓練集和測試集,進行訓練模型使鹅。
結(jié)果如下:
沒有調(diào)參的knn
利用自己生成的特征進行訓練揪阶,預測準確結(jié)果是70.2%,
利用網(wǎng)格搜索進行調(diào)參患朱,最優(yōu)參數(shù)的knn對自己生成的特征屬性預測準則率是73.1%
三鲁僚、代碼
#導入庫
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
%matplotlib inline
#導入數(shù)據(jù)
df = pd.read_csv('high_diamond_ranked_10min.csv')
print(df.head())
#查看數(shù)據(jù)的格式
print(df.shape)
#整體查看數(shù)據(jù)
print(df.info())
#查看是否有空值
print(df.isnull().sum())
#數(shù)據(jù)紅藍方的勝利場次
print(df.blueWins.value_counts())
#通過畫圖來分析數(shù)據(jù)
#箱線圖
#畫箱線圖查看藍方插眼數(shù)的離散值
df['blueWardsPlaced'].plot(kind='box')
plt.show()
#對超過實際情況的值進行替換,十分鐘最多眼數(shù)也就28左右裁厅,那些超過的應該是一整場的數(shù)據(jù)了
def get_WardsPlaced(data,name):
print(len(df[data> 22])) #有1978行數(shù)據(jù)偏離事實
x_bar = data.mean() #均值
x_std = data.std() #標準差
#統(tǒng)計量
print("未處理前的統(tǒng)計量:",data.describe())
p75 = data.quantile(q=0.75)
df[name] = data
df.loc[data > 28,name] = p75
print("進行值替換后再查看其統(tǒng)計量:",df[name].describe())
get_WardsPlaced(df['blueWardsPlaced'],'blueWardsPlaced_new')
#替換完數(shù)據(jù)再畫箱線圖查看其偏離程度
df['blueWardsPlaced_new'].plot(kind='box')
plt.show()
#有38行數(shù)據(jù)不符合實際要求冰沙,被排眼數(shù)大于插眼數(shù)
print(len(df[df['blueWardsDestroyed'] > df['blueWardsPlaced_new']]))
df.drop(index=df[df['blueWardsDestroyed'] > df['blueWardsPlaced_new']].index,axis=1,inplace=True)
#對紅色方的偏離的插眼數(shù)進行替換
get_WardsPlaced(df['redWardsPlaced'],'redWardsPlaced_new')
#對被排眼數(shù)大于插眼的數(shù)據(jù)進行整行刪除,共37行
print("\n被排眼數(shù)大于插眼數(shù):",len(df[df['redWardsDestroyed'] > df['redWardsPlaced_new']]))
df.drop(index=df[df['redWardsDestroyed'] > df['redWardsPlaced_new']].index, axis=1, inplace=True)
#雙方經(jīng)濟差散點圖
#查看藍色方經(jīng)濟差優(yōu)勢最大是多少被紅方翻盤
print(df.blueGoldDiff.where(df.blueWins == 0).sort_values(ascending=False).head(1))
#查看紅色方經(jīng)濟差優(yōu)勢最大是多少被藍方翻盤
print(df.redGoldDiff.where(df.blueWins == 1).sort_values(ascending=False).head(1))
#雙方經(jīng)濟差可視化
plt.rcParams['font.sans-serif']='SimHei' #顯示中文
plt.rcParams['axes.unicode_minus'] = False #顯示負號
plt.scatter(df.blueGoldDiff.where(df.blueWins == 0),df.blueWins.where(df.blueWins == 0),label='紅方勝利')
plt.scatter(df.blueGoldDiff.where(df.blueWins == 1),df.blueWins.where(df.blueWins == 1),label='藍方勝利')
plt.ylim(-0.1,1.1)
#plt.scatter(df.redGoldDiff,df.blueWins,c=df.blueWins)
plt.xlabel('雙方經(jīng)濟差')
plt.ylabel('紅方勝利 藍方勝利')
plt.legend(loc='upper left')
plt.vlines(-6324,-1,2,color='red',linestyle=':')
plt.vlines(6744,-1,2,color='red',linestyle=':')
plt.show()
#雙方經(jīng)驗差散點圖
#藍色方經(jīng)驗領(lǐng)先5355還輸了
print(df.blueExperienceDiff.where(df.blueWins == 0).sort_values(ascending=False).head(1))
#紅色方失敗的居最高領(lǐng)先經(jīng)驗4619
print(df.redExperienceDiff.where(df.blueWins == 1).sort_values(ascending=False).head(1))
plt.scatter(df.blueExperienceDiff.where(df.blueWins == 0),df.blueWins.where(df.blueWins == 0),label='紅方勝利')
plt.scatter(df.blueExperienceDiff.where(df.blueWins == 1),df.blueWins.where(df.blueWins == 1),label='藍方勝利')
plt.xlabel('')
plt.ylim(-0.1,1.1)
plt.xlabel('blueExperienceDiff')
plt.ylabel('紅方勝利 藍方勝利')
plt.vlines(5355,-1,2,color='r',linestyle=':')
plt.vlines(-4619,-1,2,color='red',linestyle=':')
plt.legend()
plt.show()
#平均等級散點圖
plt.scatter(df.blueAvgLevel,df.redAvgLevel,c=df.blueWins,label='等級差')
plt.legend()
plt.xlabel('blueAvgLevel')
plt.ylabel('redAvgLevel')
plt.ylim(4.5,8.5)
plt.xlim(4.3,8.2)
plt.vlines(7.0, 3, 9,color='red',linestyle=':')
plt.vlines(6.4,3,9, color='red', linestyle=':')
plt.hlines(6.0,4,8.5,color='red',linestyle=':')
plt.show()
#雙方擊殺數(shù)散點圖
#當藍色方擊殺超過9人時执虹,雙方勝利的可能性
print(df.blueWins.where(df.blueKills >= 9).value_counts(normalize=True))
print(df.blueWins.where(df.redKills >= 9).value_counts(normalize=True))
#根據(jù)擊殺數(shù)來畫散點圖拓挥,觀察大概位于哪個區(qū)間藍色方勝利
plt.scatter(df['blueKills'],df['redKills'],c=df.blueWins)
plt.xlim(-0.8,23.2)
plt.ylim(-0.8,23.2)
plt.hlines(9,-1,24, color='red',linestyle=':')
plt.vlines(9,-1,24,color='red',linestyle=':')
plt.xlabel('bluekill')
plt.ylabel('redkill')
plt.show()
#雙方視野散點圖
#根據(jù)雙方視野來繪制散點圖
plt.scatter(df['blueWardsPlaced_new'] - df['blueWardsDestroyed'],df['redWardsPlaced_new'] - df['redWardsDestroyed'],c=df.blueWins)
plt.xlabel('blue_view')
plt.ylabel('red_view')
plt.show()
#生成需要的特征變量
data_new = pd.DataFrame()
data_new['blueWins'] = df['blueWins']
#藍色方實際的眼數(shù)
data_new['blue_view'] = df['blueWardsPlaced_new'] - df['blueWardsDestroyed']
#藍色方被摧毀的防御塔
data_new['blueTowersDestroyed'] = df['blueTowersDestroyed']
#藍色方的KDA
data_new['blueKDA'] = (df['blueKills']+df['blueAssists'])/df['blueDeaths']
#藍色方每次擊殺的參與人數(shù)
data_new['blue_num_pkill'] = (df['blueAssists']+df['blueKills'])/df['blueKills']
#藍色方的擊殺小龍數(shù)
data_new['blueEliteMonsters'] = df['blueEliteMonsters']
#雙方的經(jīng)濟差
data_new['blueGoldDiff'] = df['blueGoldDiff']
#雙方的平均等級差
data_new['grade_Diff'] = df['blueAvgLevel'] - df['redAvgLevel']
data_new['red_view'] = df['redWardsPlaced_new'] - df['redWardsDestroyed']
data_new['redTowersDestroyed'] = df['redTowersDestroyed']
data_new['redKDA'] = (df['redKills']+df['redAssists'])/df['redDeaths']
data_new['red_num_pkill'] = (df['redAssists']+df['redKills'])/df['redKills']
data_new['redEliteMonsters'] = df['redEliteMonsters']
print(data_new.head())
#對新特征進行清洗
#對運算后為nan值的行進行替換
data_new['blue_num_pkill'] = data_new['blue_num_pkill'].fillna(0)
data_new['red_num_pkill'] = data_new['red_num_pkill'].fillna(0)
#將無窮大進行替換,replace將在原數(shù)據(jù)的副本上操作,所以時間比較慢
data_new['blueKDA'] = data_new['blueKDA'].replace(np.inf,(df['blueKills']+df['blueAssists']))
data_new['redKDA'] = data_new['redKDA'].replace(np.inf,(df['redKills']+df['redAssists']))
#print(data_new['blue_num_pkill'].isnull().sum())
#print(data_new['red_num_pkill'].isnull().sum())
print(data_new['blueKDA'].describe())
#建立模型
#對數(shù)據(jù)進行劃分
x_train,x_test,y_train,y_test = train_test_split(data_new.iloc[:,1:],data_new['blueWins'],test_size=0.2,random_state=1)
print("訓練集前五行:",x_train.head())
print("訓練集結(jié)果前五行:",y_train.head())
print("測試集前五行:",x_test.head())
print("測試集結(jié)果前五行:",y_test.head())
#建立最普通的knn算法,70.2%
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
#訓練模型
knn.fit(x_train,y_train)
#預測結(jié)果
y_predicted = knn.predict(x_test)
#計算準確率
accuracy = np.mean(y_predicted == y_test)*100
print("當前分類評估器是:knn")
print("當前Accuracy是:%.1f"%accuracy+"%")
#使用網(wǎng)格搜素袋励,尋找最優(yōu)參數(shù)
knn_params = {"n_neighbors" :np.arange(95,105),
"weights" : ["distance"],
"algorithm" : ["ball_tree"] , #,"kd_tree","brute"
"leaf_size":[1,2]}
knn = KNeighborsClassifier()
grid_knn = GridSearchCV(knn, knn_params,cv=5,verbose=2,n_jobs=-1)
grid_knn.fit(x_train,y_train)
knn_best_params=grid_knn.best_params_
print(knn_best_params)
#最優(yōu)參數(shù)knn的結(jié)果使用data_new數(shù)據(jù)
knn = KNeighborsClassifier(algorithm="ball_tree",n_neighbors=98,weights="distance",leaf_size=1)
knn.fit(x_train,y_train)
y_predicted = knn.predict(x_test)
accuracy = np.mean(y_predicted == y_test)*100
print("當前分類評估器是:knn")
print("當前Accuracy是:%.1f"%accuracy+"%")
四侥啤、總結(jié)
上面的預測準確率只有73%左右当叭,我認為主要的原因是這只是前十分鐘的數(shù)據(jù)并且還缺少了很多的特征,就如上面所提到的視野問題愿棋、甚至雙方選擇的英雄也有影響(前期英雄或者后期英雄等)科展,每個英雄的優(yōu)勢期都不一樣、以及打野的意識等這些很難通過數(shù)據(jù)表現(xiàn)出來糠雨,所以導致模型預測率不是很理想才睹。
一開始選擇這個數(shù)據(jù)做預測的時候只是為了完成實驗報告,自己下載好數(shù)據(jù)后進行一頓操作后發(fā)現(xiàn)預測率卡在了70%左右甘邀,這其實不是很理想琅攘,然后自己就開始通過生成更多的特征來試圖提高預測率,但是并沒有用松邪,后來實在沒辦法上網(wǎng)一搜發(fā)現(xiàn)居然有人已經(jīng)做了這份數(shù)據(jù)的分析坞琴。
所以最后借鑒了大佬的方法利用網(wǎng)格調(diào)參終于讓準確度有了點點增加,同時通過大佬的代碼也學到了利用畫圖來將數(shù)據(jù)的特點呈現(xiàn)出來而不是僅僅靠輸出數(shù)據(jù)來觀察逗抑。
不過我的特征變量與大佬不一樣剧辐,當然我也嘗試結(jié)合了兩個人生成的特征變量來訓練模型,可惜并沒有提高邮府,可能是變量的相關(guān)性太高了荧关,因為大佬做的是對擊殺助攻經(jīng)濟差距的處理,而我是通過視野褂傀,每次擊殺參與人數(shù)等雖然我沒對經(jīng)濟差有什么處理忍啤,但是我的特征中也是傳入了經(jīng)濟差的,才會導致加入新特征后結(jié)果還是沒有什么進步仙辟。