kaggle——絕地求生游戲最終排名預測


絕地求生游戲最終排名預測

知識點

  • 數據讀取與預覽

  • 數據可視化

  • 構建隨機森林預測模型

導入數據並預覽

先導入數據並預覽。本次實驗同樣來源於 Kaggle 上的一個競賽: ,由於原始數據較大,我們只取了其中一部分的數據來進行分析。如果你想分析所有的數據可以去

讀取數據並預覽前5行

import pandas as pd

df = pd.read_csv('train.csv')
df.head()

由上面的輸出結果可知,數據主要由 29 列構成。我們所要預測的列為 winPlacePerc 。各列所表示的含義如下。

  • DBNOs - 擊倒多少敵人
  • assists - 傷害過多少敵人(最終該敵人被隊友殺害)
  • boosts - 使用過多少個提升性的物品 (boost items used)
  • damageDealt - 造成的總傷害-自己所受的傷害
  • headshotKills - 通過爆頭而殺死的敵人數量
  • heals - 使用了多少救援類物品
  • Id - 玩家ID
  • killPlace - 殺死敵人數量的排名
  • killPoints - 基於殺戮的玩家外部排名。將其視為Elo排名,只有殺死才有意義。如果 rankPoints 中的值不是 -1,那么 killPoints 中的任何 0 都應被視為“無”。
  • killStreaks - 短時間內殺死敵人的最大數量
  • kills - 殺死的敵人的數量
  • longestKill - 玩家和玩家在死亡時被殺的最長距離。 這可能會產生誤導,因為擊倒一名球員並開走可能會導致最長的殺戮統計數據。
  • matchDuration - 匹配用了多少秒
  • matchId - 匹配的 ID(每一局一個 ID)
  • matchType - 單排/雙排/四排;標准模式是 “solo”,“duo”,“squad”,“solo-fpp”,“duo-fpp”和“squad-fpp”; 其他模式來自事件或自定義匹配。
  • rankPoints - 類似 Elo 的玩家排名。 此排名不一致,並且在 API 的下一個版本中已棄用,因此請謹慎使用。值 -1 表示“無”。
  • revives - 玩家救援隊友的次數
  • rideDistance - 玩家使用交通工具行駛了多少米
  • roadKills - 在交通工具上殺死了多少玩家
  • swimDistance - 游泳了多少米
  • teamKills - 該玩家殺死隊友的次數
  • vehicleDestroys - 毀壞了多少交通工具
  • walkDistance - 步行運動了多少米
  • weaponsAcquired - 撿了多少把槍
  • winPoints - 基於贏的玩家外部排名。將其視為 Elo 排名,只有獲勝才有意義。如果 kPoints 中的值不是 -1,那么 winPoints 中的任何 0 都應被視為“無”。
  • groupId - 隊伍的 ID。 如果同一組玩家在不同的比賽中比賽,他們每次都會有不同的 GroupId。
  • numGroups - 在該局比賽中有玩家數據的隊伍數量
  • maxPlace - 在該局中已有數據的最差的隊伍名詞(可能與該局隊伍數不匹配,因為數據收集有跳躍)
  • winPlacePerc - 預測目標,是以百分數計算的,介於 0-1 之間,1 對應第一名,0 對應最后一名。 它是根據 maxPlace 計算的,而不是 numGroups ,因此匹配中可能缺少某些隊伍。
# 現在查看一下數據的基本信息。
df.info()
# 由上可知,該數據集中不含有缺失值,查看數據描述。
df.describe()

數據可視化

# 由於我們所要預測的列為 winPlacePerc ,即排名情況,所以先來分析該列。先導入相關的畫圖工具。
import seaborn as sns
from matplotlib import pyplot as plt
%matplotlib inline
plt.style.use('fivethirtyeight')
#winPlacePerc 列是系統給出的游戲排名,而 winPoints 是外部給出的游戲排名,現在畫出這兩列的數據分布圖。

fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_figwidth(15)
sns.distplot(df['winPlacePerc'], ax=ax1)
sns.distplot(df['winPoints'], ax=ax2)
plt.show()

img

從上的結果可以看到,游戲排名似乎呈兩極分化現象,0 和 1 兩頭的人數都相對多一點。

# 現在來看玩家擊倒敵人的人數的情況。
train_dbno = pd.DataFrame(df['DBNOs'].value_counts(), columns=['DBNOs'])
dbno = train_dbno.iloc[:9, :]
dbno.iloc[8]['DBNOs'] = train_dbno.iloc[8:, :].sum()['DBNOs']
plt.figure(figsize=(14, 5))
sns.barplot(dbno.index, dbno.DBNOs)
plt.gca().set_xticklabels([0, 1, 2, 3, 4, 5, 6, 7, '8+'])
plt.gca().set_xlabel('No of enemy players knocked')
plt.gca().set_ylabel("count")
plt.show()
plt.savefig("enemy_")

img

從上圖可以看到,擊倒敵人的數量越多,排名也就越高。這說明,擊倒敵人與排名有很大的關系。


fig, (ax1, ax2) = plt.subplots(figsize=(15, 5))
sns.pointplot(x='DBNOs', y='winPlacePerc', data=df, alpha=0.8)
plt.xlabel('Number of DBNOs', fontsize=15, color='blue')
plt.ylabel('Win Percentage', fontsize=15, color='blue')
plt.title('DBNOs / Win Ratio', fontsize=20, color='blue')
plt.grid()
plt.show()

img

從上圖可以看到,擊倒敵人的數量越多,排名也就越高。這說明,擊倒敵人與排名有很大的關系。

# 現在看在一局游戲中,玩家自己所受到的傷害。
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_figwidth(15)

sns.distplot(df['damageDealt'], ax=ax1)
sns.boxplot(df['damageDealt'], ax=ax2)
plt.show()

img

從上圖可以看出,大多數人的受到的傷害在 0 到 500 之間。現在來看玩家受傷害值是否與排名有關系。

# 將傷害值分為 6 個部分。
# pd.cut()
data = df.copy()
data['damageDealt_rank'] = pd.cut(data['damageDealt'],
                                 [-1,500,1000,1500,2000,2500,60000],
                                 labels=['0-500','500-1000','1000-1500',
                                        '1500-2000','2000-2500','2500+'])
f,ax1 = plt.subplots()
sns.pointplot(x='damageDealt_rank', y='winPlacePerc', data=data,alpha=0.8)
plt.xlabel('damageDealtk', fontsize=15, color='blue')
plt.xticks(rotation=45)
plt.ylabel('Win Percentage', fontsize=15, color='blue')
plt.title('damageDealt / Win Ratio', fontsize=20, color='blue')
plt.grid()
plt.show()

img

從上圖可以看到,玩家排名越靠前,所受到的傷害就越大。

現在來看殺死敵人的排名情況。

plt.figure()
sns.distplot(df['killPlace'], bins=50)
plt.show()

img

從上圖可以看出,在殺死敵人排名中呈現均勻分布的現象。

現看一下殺死敵人的數量。

plt.figure()
sns.distplot(df['killPlace'])
plt.show()

img

從上圖可以看出,在殺死敵人排名中呈現均勻分布的現象。

現看一下殺死敵人的數量。

# sns.regplot
plt.figure()
sns.regplot(df['kills'].values, df['damageDealt'].values)
plt.gca().set_ylabel('Damage dealt')
plt.gca().set_xlabel('Total kills')
plt.show()

img

從上圖可以看到,大多數玩家殺死敵人的數量都不超過 5 個人。從右圖看到,有個別玩家在游戲中殺死敵人的數量超多了 20 人。

我們可以分析一下,游戲玩家殺死敵人的數量與自己所受到的傷害的關系。

plt.figure()
sns.regplot(df['kills'].values, df['damageDealt'].values)
plt.gca().set_ylabel('Damage dealt')
plt.gca().set_xlabel('Total kills')
plt.show()

從上圖可以看到,一個玩家殺死敵人的數量越多,自己所受到的傷害就越大,基本呈線性關系。

現在分析一下玩家殺死敵人的數量與排名的關系。

data = df.copy()
# 將殺死敵人的數量分為 6 個部分。
data['kills_rank'] = pd.cut(data['kills'], [-1, 0, 2, 5, 10, 20, 60],
                            labels=['0_kills', '1-2_kills', '3-5_kills',
                                    '6-10_kills', '11-20_kills', '20+kills'])
plt.figure(figsize=(10, 4))
sns.boxplot(x='kills_rank', y='winPlacePerc', data=data)
plt.show()

從上圖可以看到,玩家殺死敵人的數量越多,其最后的排名也就越高。

再來看一下玩家在游戲中,一槍爆頭的個數。

fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_figwidth(15)
sns.distplot(df['headshotKills'], ax=ax1)
sns.boxplot(df['headshotKills'], ax=ax2)
plt.show()

img

從上圖可看到,大多數玩家都沒有一槍爆頭。但在右圖中,有個別玩家一槍爆頭的數量到達了 8 人。

現在看一下,爆頭人數與排名之間的關系。

f, ax1 = plt.subplots(figsize=(14, 4))
sns.pointplot(x='headshotKills', y='winPlacePerc', data=df, alpha=0.8)
plt.xlabel('Number of headshotKills', fontsize=15, color='blue')
plt.ylabel('Win Percentage', fontsize=15, color='blue')
plt.title('headshotKills/ Win Ratio', fontsize=20, color='blue')
plt.grid()
plt.show()

img

可以查看一下短時間內殺死敵人的數量。

killstreak = pd.DataFrame(df['killStreaks'].value_counts())
killstreak.iloc[4] = killstreak.iloc[4:].sum()
killstreak = killstreak[:5]
sns.barplot(killstreak.index, killstreak['killStreaks'])

img

data = df.copy()
data['move'] = data['rideDistance']+data['swimDistance']+data['walkDistance']
sns.distplot(data['move'])

img

fig, (ax1, ax2) = plt.subplots(1, 2) 
fig.set_figwidth(15) 
sns.distplot(df['weaponsAcquired'], ax=ax1) 
sns.boxplot(df['weaponsAcquired'], ax=ax2)

img

f, ax1 = plt.subplots(figsize=(15, 5))
sns.pointplot(x='weaponsAcquired', y='winPlacePerc', data=df, alpha=0.8)
plt.xlabel('Number of weaponsAcquired', fontsize=15, color='blue')
plt.ylabel('Win Percentage', fontsize=15, color='blue')
plt.title('weaponsAcquired/ Win Ratio', fontsize=20, color='blue')
plt.grid()
plt.show()

img

# heals - 使用了多少救援類物品
# boosts - 使用過多少個提升性的物品 (boost items used)
data = df.copy()
f, ax1 = plt.subplots(figsize=(14, 4))
sns.pointplot(x='heals', y='winPlacePerc', data=data, color='lime', alpha=0.8)
sns.pointplot(x='boosts', y='winPlacePerc', data=data, color='blue', alpha=0.8)
plt.text(0, 0.9, 'Heals', color='lime', fontsize=17, style='italic')
plt.text(0, 0.85, 'Boosts', color='blue', fontsize=17, style='italic')
plt.xlabel('Number of heal/boost items', fontsize=15, color='blue')
plt.ylabel('Win Percentage', fontsize=15, color='blue')
plt.title('Heals vs Boosts', fontsize=20, color='blue')
plt.grid()

img

特征工程

上面只是對數據集中的一些特征列進行了可視化,以便更好的理解數據。而我們的任務是根據這些特征來預測玩家的排名。現在我們對數據進行手工提取特征。

def dispose(df):
    # 救援類物品和提升性能類物品都可以算作是一類,因此將這兩者加起來得到一個新的特征列。同樣的方法對距離進行處理。
    df['healsAndBoosts'] = df['heals']+df['boosts']
    df['totalDistance'] = df['walkDistance']+df['rideDistance']+df['swimDistance']
    # 當使用提升類物品時,游戲玩家可以運行得更快。同時也幫助玩家保持在區外。因此,我們可以創建一個特征列,用來記錄游戲玩家沒走一步所消耗的提升性物品。救援類物品雖然不會使玩家跑得更快,但也有助於保持遠離危險地帶。所以讓我們也為救援類物品創建相同的特征列。
    df['boostsPerWalkDistance'] = df['boosts'] / \
    (df['walkDistance']+1)  # 加 1 是為了防止分母為 0
    df['boostsPerWalkDistance'].fillna(0, inplace=True)
    df['healsPerWalkDistance'] = df['heals']/(df['walkDistance']+1)
    df['healsPerWalkDistance'].fillna(0, inplace=True)
    df['healsAndBoostsPerWalkDistance'] = df['healsAndBoosts'] / \
        (df['walkDistance']+1)
    df['healsAndBoostsPerWalkDistance'].fillna(0, inplace=True)
    df[['walkDistance', 'boosts', 'boostsPerWalkDistance', 'heals',
        'healsPerWalkDistance', 'healsAndBoosts', 'healsAndBoostsPerWalkDistance']][40:45]
    # 提取殺死敵人的數量與步行距離的關系。
    df['killsPerWalkDistance'] = df['kills'] / \
    	(df['walkDistance']+1)  # 加 1 是為了防止分母為 0
	df['killsPerWalkDistance'].fillna(0, inplace=True)
	df[['kills', 'walkDistance', 'rideDistance','killsPerWalkDistance', 'winPlacePerc']].tail(5)  
    return df

df=dispose(df)

構建模型

先來看一下我們的數據。

df.head()

從上圖可以看到,此時的數據包含 36 列。但玩家編號(Id)、分組編號(groupId)、游戲局編號(matchId)、游戲的類型(matchType)對預測結果是沒有幫助的。因此現在將這四列刪除掉。

def df_drop(df):
    df_drop = df.drop(['Id', 'groupId', 'matchId', 'matchType'], axis=1)

df_drop = df_drop(df)

划分訓練集和測試集。

from sklearn.model_selection import train_test_split

data_X = df_drop.drop(['winPlacePerc'], axis=1)
data_y = df_drop['winPlacePerc']

構建預測模型。這里我們使用隨機森林回歸的方法來構建模型。

from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor(n_estimators=40)  # 構建模型
model.fit(data_X, data_y)  # 訓練模型

test_X = pd.read_csv('test_C2.csv')
dispos(test_X)
y_pred = model.predict(test_X)  # 預測
y_pred[:10]

上面我們完成了預測模型的構建預訓練,並對測試集進行預測。為了直觀的看出模型預測的好壞,現在通過畫圖的方法來對比。

f, ax1 = plt.subplots(figsize=(15, 5))

plt.plot(test_y[:100])
plt.plot(y_pred[:100])

在上圖中,藍色線條表示測試數據的真實值,而紅色線條表示預測的數據。從圖中可以看出,我們所構建的模型基本能夠預測正確。現在查看一下均方誤差。

from sklearn.metrics import mean_squared_error

mean_squared_error(y_pred, test_y)






還有較多bug,未完待續。。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM