绝地求生游戏最终排名预测--kaggle比赛

绝地求生游戏最终排名预测

知识点

  • 数据读取与预览

  • 数据可视化
  • 构建随机森林预测模型

导入数据并预览

先导入数据并预览。本次实验同样来源于 Kaggle 上的一个竞赛: ,由于原始数据较大,我们只取了其中一部分的数据来进行分析。如果你想分析所有的数据可以去

读取数据并预览前5行

import pandas as pd

df = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/1363/PUBG.csv')
df.head()
Id groupId matchId assists boosts damageDealt DBNOs headshotKills heals killPlace ... revives rideDistance roadKills swimDistance teamKills vehicleDestroys walkDistance weaponsAcquired winPoints winPlacePerc
0 7f96b2f878858a 4d4b580de459be a10357fd1a4a91 0 0 0.00 0 0 0 60 ... 0 0.0000 0 0.00 0 0 244.80 1 1466 0.4444
1 eef90569b9d03c 684d5656442f9e aeb375fc57110c 0 0 91.47 0 0 0 57 ... 0 0.0045 0 11.04 0 0 1434.00 5 0 0.6400
2 1eaf90ac73de72 6a4a42c3245a74 110163d8bb94ae 1 0 68.00 0 0 0 47 ... 0 0.0000 0 0.00 0 0 161.80 2 0 0.7755
3 4616d365dd2853 a930a9c79cd721 f1f1f4ef412d7e 0 0 32.90 0 0 0 75 ... 0 0.0000 0 0.00 0 0 202.70 3 0 0.1667
4 315c96c26c9aac de04010b3458dd 6dc8ff871e21e6 0 0 100.00 0 0 0 45 ... 0 0.0000 0 0.00 0 0 49.75 2 0 0.1875

5 rows × 29 columns

由上面的输出结果可知,数据主要由 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)

完毕。

猜你喜欢

转载自www.cnblogs.com/wwj99/p/12080404.html