uratatsu
スプラトゥーン2を普段やっているので、ドメイン知識を使ってブキ/ルールごとの勝率の違いや強いと言われているスペシャルなどについて、いくつか検証をしてみました。参考になればと思います。
特徴量として使用して精度が上がるかどうかの検証はしていません。
また今回のデータは5.0.1環境でのものであり、今回のコンペティションで与えられたデータに限るもので、現環境での分析とは異なるものであることにご留意ください。
※追記
https://stat.ink/downloads
上記サイトに記載のある通り、A1はstat.inkを利用している投稿者であり、バイアスがかかっている可能性があるため、A1を集計から排除しました。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
train = pd.read_csv('../input/train_data.csv')
test = pd.read_csv('../input/test_data.csv')
#https://prob.space/competitions/game_winner/discussions/e-toppo-Post0082a60376ef134af3a4
buki_data = pd.read_csv('../input/statink-weapon2.csv')
buki_data.loc[buki_data['category2'] == 'maneuver', 'category2'] = 'maneuver_cat' # カテゴリ名称とブキ名称が被るためカテゴリ名称を変更する
#https://prob.space/competitions/game_winner
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
mlb.fit([set(train['A1-weapon'].unique())])
MultiLabelBinarizer(classes=None, sparse_output=False)
def trans_weapon(df, columns=['A1-weapon', 'A2-weapon', 'A3-weapon', 'A4-weapon']):
weapon = df.fillna('none')
weapon_binarized = mlb.transform(weapon[columns].values)
return pd.DataFrame(weapon_binarized, columns=mlb.classes_)
def make_input_output(df, with_y=False):
#a_weapon = trans_weapon(df, ['A1-weapon', 'A2-weapon', 'A3-weapon', 'A4-weapon'])
a_weapon = trans_weapon(df, ['A2-weapon', 'A3-weapon', 'A4-weapon'])
b_weapon = trans_weapon(df, ['B1-weapon', 'B2-weapon', 'B3-weapon', 'B4-weapon'])
a_weapon = a_weapon.add_suffix('_A')
b_weapon = b_weapon.add_suffix('_B')
X = pd.concat([a_weapon, b_weapon], axis=1)
if with_y:
y = df['y']
return X, y
return X
X = make_input_output(train, with_y=False)
train = pd.concat([train, X], axis=1)
まずはブキの勝率から。
def win_rate(buki, df):
count = (len(df[df[buki + '_A'] == 1]) + len(df[df[buki + '_B'] == 1])) # それぞれのチームで対象ブキが出現する試合数のカウント
win = (len(df[(df[buki + '_A'] == 1) & (df.y == 1)]) +len((df[(df[buki + '_B'] == 1) & (df.y == 0)]))) # それぞれのチームで対象ブキが出現する試合のうち各チームが勝利する試合数のカウント
rate = win / count
return rate, count
win_rate_df = []
for buki in buki_data.key.unique():
#print(buki, win_rate(buki))
rate, count = win_rate(buki, train)
win_rate_df.append([buki, rate, count])
win_rate_df = pd.DataFrame(win_rate_df, columns=['buki', 'win_rate', 'count'])
win_rate_df = win_rate_df.sort_values(by= 'win_rate', ascending=False)
buki_ja_dict = buki_data[['key','[ja-JP]']].set_index('key').to_dict()['[ja-JP]']
win_rate_df.buki = win_rate_df.buki.map(buki_ja_dict)
win_rate_df = win_rate_df.reset_index(drop=True)
win_rate_df.head(20)
buki | win_rate | count | |
---|---|---|---|
0 | L3リールガン | 0.543564 | 4568 |
1 | ヒーローブラシ レプリカ | 0.526882 | 651 |
2 | ヒーローシェルター レプリカ | 0.526080 | 671 |
3 | パラシェルターソレーラ | 0.523271 | 1504 |
4 | ダイナモローラー | 0.522659 | 662 |
5 | キャンピングシェルターカーモ | 0.522313 | 2196 |
6 | ボールドマーカー7 | 0.521902 | 6689 |
7 | ダイナモローラーベッチュー | 0.521407 | 6680 |
8 | ダイナモローラーテスラ | 0.520404 | 2475 |
9 | ヒーローローラー レプリカ | 0.518670 | 5865 |
10 | ヒーローブラスター レプリカ | 0.517820 | 477 |
11 | デュアルスイーパーカスタム | 0.516470 | 16758 |
12 | パブロ・ヒュー | 0.516342 | 3182 |
13 | .96ガロン | 0.515828 | 2464 |
14 | バケットスロッシャーソーダ | 0.515443 | 3011 |
15 | シャープマーカー | 0.515213 | 1972 |
16 | バケットスロッシャーデコ | 0.515111 | 2217 |
17 | ハイドラントカスタム | 0.513648 | 7437 |
18 | パーマネント・パブロ | 0.513076 | 803 |
19 | バレルスピナーリミックス | 0.512966 | 6864 |
win_rate_df[(win_rate_df['buki'] == 'パラシェルター') | (win_rate_df['buki'] == 'ホクサイ')]
buki | win_rate | count | |
---|---|---|---|
78 | パラシェルター | 0.487106 | 1047 |
94 | ホクサイ | 0.480755 | 1377 |
全体ではL3リールガンの勝率がトップでした。頭一つ抜けている感じがします。2位3位にヒーロー種が来ているのが面白いです。 同じ性能のパラシェルターはwin_rate 0.449、ホクサイはwin_rate0.483となっています。ヒーロー種はヒーローモードをある程度やりこまないと手に入らないため、それをあえて使う人はやりこんでいる人だとみなせるかもしれません。
※ヒーロー種とはヒーローモードという1人プレイモードで手に入るブキのことを言い、各ブキ種にひとつづつあり、それぞれの無印と見た目が違うだけで、性能やサブ、スペシャルは全く同一になっています。
win_rate_df.tail(20)
buki | win_rate | count | |
---|---|---|---|
119 | プライムシューター | 0.460609 | 1904 |
120 | スクイックリンγ | 0.459928 | 1672 |
121 | スプラスコープ | 0.459258 | 4553 |
122 | スクリュースロッシャー | 0.458333 | 408 |
123 | バレルスピナー | 0.457627 | 885 |
124 | スプラチャージャーコラボ | 0.453311 | 2431 |
125 | スプラマニューバー | 0.453254 | 2674 |
126 | スプラチャージャーベッチュー | 0.451841 | 3395 |
127 | ジェットスイーパーカスタム | 0.450923 | 2058 |
128 | ヒーローチャージャー レプリカ | 0.442942 | 4215 |
129 | ラピッドブラスター | 0.439210 | 658 |
130 | スプラチャージャー | 0.436674 | 6277 |
131 | ノヴァブラスターネオ | 0.435294 | 340 |
132 | ソイチューバーカスタム | 0.429668 | 391 |
133 | ラピッドブラスターデコ | 0.428571 | 364 |
134 | ソイチューバー | 0.419453 | 658 |
135 | 4Kスコープカスタム | 0.404537 | 529 |
136 | スクイックリンα | 0.404118 | 1700 |
137 | Rブラスターエリートデコ | 0.402439 | 328 |
138 | リッター4Kカスタム | 0.382166 | 628 |
勝率最下位はリッター4Kカスタムでした。サブ/スペシャルが長射程ブキと噛み合っていないからでしょうか。あまり使い慣れた人がいない印象はあります。
全体的にチャージャー種やバレル、ブラスターなど扱いが難しいブキが下位ランキングに入っている印象です。意外だったのはジェットスイーパーカスタムがワーストに入っていることでした。上手い人たちの対抗戦や大会でもよく見ますし、ホコやヤグラにおいてハイパープレッサーで無双してる印象がありますが、今回のデータの範囲では勝率は高くないようです。
スプラトゥーン2にはナワバリバトル、ガチエリア、ガチヤグラ、ガチホコ、ガチアサリの5つのルールが存在します。それぞれ有利、不利なブキがあると言われていますが、その差を見てみます。
win_rate_mode_df = pd.DataFrame()
for mode in train['mode'].unique():
win_rate_df = []
train_mode = train[train['mode'] == mode]
for buki in buki_data.key.unique():
#print(buki, win_rate(buki))
rate, count = win_rate(buki, train_mode)
win_rate_df.append([buki, rate, count])
win_rate_df = pd.DataFrame(win_rate_df, columns=[mode + '_buki', mode + '_win_rate', mode + '_count'])
win_rate_df = win_rate_df.sort_values(by= mode + '_win_rate', ascending=False)
buki_ja_dict = buki_data[['key','[ja-JP]']].set_index('key').to_dict()['[ja-JP]']
win_rate_df[mode + '_buki'] = win_rate_df[mode + '_buki'].map(buki_ja_dict)
win_rate_df = win_rate_df.reset_index(drop=True)
win_rate_mode_df = pd.concat([win_rate_mode_df, win_rate_df], axis=1)
win_rate_mode_df.head(10)
nawabari_buki | nawabari_win_rate | nawabari_count | hoko_buki | hoko_win_rate | hoko_count | yagura_buki | yagura_win_rate | yagura_count | area_buki | area_win_rate | area_count | asari_buki | asari_win_rate | asari_count | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | L3リールガン | 0.595882 | 777 | ヒーローブラシ レプリカ | 0.602941 | 136 | ロングブラスターカスタム | 0.602740 | 73 | ヒーローシェルター レプリカ | 0.594595 | 148 | .52ガロンデコ | 0.581818 | 55 |
1 | ヒーロースロッシャー レプリカ | 0.571429 | 84 | バケットスロッシャーソーダ | 0.557845 | 631 | スクリュースロッシャーネオ | 0.572727 | 110 | ダイナモローラー | 0.566667 | 60 | H3リールガンD | 0.567961 | 206 |
2 | L3リールガンD | 0.565859 | 949 | パラシェルター | 0.556291 | 151 | キャンピングシェルターカーモ | 0.552511 | 219 | スパイガジェットソレーラ | 0.551181 | 127 | ケルビン525 | 0.564417 | 163 |
3 | H3リールガン | 0.562162 | 185 | パラシェルターソレーラ | 0.555556 | 351 | ヒーロースロッシャー レプリカ | 0.552239 | 67 | ヒーローシューター レプリカ | 0.549801 | 251 | ハイドラント | 0.564103 | 156 |
4 | バケットスロッシャーソーダ | 0.548913 | 552 | ボトルガイザー | 0.547619 | 42 | ヒーローブラスター レプリカ | 0.546512 | 172 | ダイナモローラーベッチュー | 0.543961 | 2070 | バレルスピナー | 0.561798 | 89 |
5 | パブロ・ヒュー | 0.547983 | 719 | バレルスピナー | 0.540636 | 283 | バレルスピナーリミックス | 0.544069 | 1509 | ボールドマーカー7 | 0.543877 | 1037 | ダイナモローラー | 0.558824 | 68 |
6 | プライムシューターコラボ | 0.546980 | 298 | ダイナモローラーテスラ | 0.536036 | 444 | N-ZAP83 | 0.539519 | 291 | キャンピングシェルターカーモ | 0.542105 | 760 | L3リールガン | 0.558704 | 988 |
7 | スパッタリー | 0.546201 | 487 | ヒーロースピナー レプリカ | 0.535714 | 196 | スクリュースロッシャーベッチュー | 0.534171 | 1273 | バケットスロッシャーデコ | 0.538813 | 657 | スプラスピナーコラボ | 0.553571 | 112 |
8 | シャープマーカーネオ | 0.543819 | 1723 | .52ガロンデコ | 0.534591 | 159 | ダイナモローラーベッチュー | 0.530549 | 1604 | 14式竹筒銃・丙 | 0.536765 | 136 | ヒッセン | 0.547703 | 283 |
9 | シャープマーカー | 0.543668 | 458 | スプラローラーベッチュー | 0.534392 | 378 | ヒーローブラシ レプリカ | 0.529412 | 136 | ボトルガイザーフォイル | 0.534196 | 541 | パラシェルター | 0.546099 | 141 |
全ルールでトップだったL3リールガンの強さはナワバリバトルのおかげのようです。勝率6割はヤバいですね。一方のチームにしか含まれない場合などを調べるともっと高いかもしれません。 他のルールは上位に来ているもののサンプル数が少なく、ブキ以外の要因(特定のプレイヤースキルなど)に左右されている可能性があるように思えます。
インクを一定量塗ると、スペシャルゲージがたまり、スペシャルと呼ばれる特別な攻撃を放つことができます。単体で相手のイカをキルできるものや、メインブキの攻撃を補助するもの、味方の防御力を高めるものなど様々なものがあります。
それぞれルールごとに強いものや弱いものがあると言われています。
cols = ['A1-weapon', 'A2-weapon', 'A3-weapon', 'A4-weapon', 'B1-weapon', 'B2-weapon', 'B3-weapon', 'B4-weapon']
for col in cols:
train = train.merge(buki_data[['key','subweapon', 'special']], left_on=col, right_on='key', how='left').drop(columns='key').rename(columns={'subweapon':'subweapon' + '_' + col, 'special':'special' + '_' + col})
from collections import Counter
def special_count(df):
#df_A = df[['special_A1-weapon', 'special_A2-weapon', 'special_A3-weapon', 'special_A4-weapon']].values
df_A = df[['special_A2-weapon', 'special_A3-weapon', 'special_A4-weapon']].values
df_B = df[['special_B1-weapon', 'special_B2-weapon', 'special_B3-weapon', 'special_B4-weapon']].values
A_special = pd.DataFrame([Counter(x) for x in df_A]).fillna(0).astype('int8')
B_special = pd.DataFrame([Counter(x) for x in df_B]).fillna(0).astype('int8')
A_special = A_special.add_suffix('_A')
B_special = B_special.add_suffix('_B')
X = pd.concat([A_special, B_special], axis=1)
return X
X = special_count(train)
train = pd.concat([train, X], axis=1)
def win_rate_special(special, df):
count = (len(df[df[special + '_A'] != 0]) + len(df[df[special + '_B'] != 0])) # それぞれのチームで対象スペシャルが出現する試合数のカウント
win = (len(df[(df[special + '_A'] != 0) & (df.y == 1)]) +len((df[(df[special + '_B'] != 0) & (df.y == 0)]))) # それぞれのチームで対象スペシャルが出現する試合のうち各チームが勝利する試合数のカウント
rate = win / count
return rate, count
win_rate_special_df = []
for special in buki_data.special.unique():
rate, count = win_rate_special(special, train)
win_rate_special_df.append([special, rate, count])
win_rate_special_df = pd.DataFrame(win_rate_special_df, columns=['special', 'win_rate', 'count'])
win_rate_special_df = win_rate_special_df.sort_values(by= 'win_rate', ascending=False)
win_rate_special_df = win_rate_special_df.reset_index(drop=True)
win_rate_special_df
special | win_rate | count | |
---|---|---|---|
0 | ultrahanko | 0.512526 | 12614 |
1 | quickbomb_pitcher | 0.512202 | 3401 |
2 | nicedama | 0.507989 | 36674 |
3 | armor | 0.505600 | 47324 |
4 | chakuchi | 0.503061 | 62567 |
5 | sphere | 0.498743 | 34196 |
6 | amefurashi | 0.498629 | 56894 |
7 | splashbomb_pitcher | 0.498346 | 3325 |
8 | jetpack | 0.496187 | 31605 |
9 | kyubanbomb_pitcher | 0.494049 | 16049 |
10 | missile | 0.492849 | 53771 |
11 | curlingbomb_pitcher | 0.491640 | 5024 |
12 | bubble | 0.484163 | 17775 |
13 | robotbomb_pitcher | 0.482283 | 4572 |
14 | presser | 0.454795 | 20429 |
ウルトラハンコ、クイックボムピッチャーが最も勝率の高いスペシャルとなりました。クイックボムピッチャーは非常に珍しく、バケットスロッシャーソーダと14式竹筒銃・乙の2ブキのみしかこのスペシャルを使えるブキはありません。非常にキル力の高いスペシャルと言われています。最下位はハイパープレッサーとなりました。ハイパープレッサーはスペシャルの性質上、後衛ブキが持つことが多く、チャージャーやジェットスイーパーカスタムの勝率の低さを反映していると思われます。
それぞれのスペシャルの性質上、ルールごとに有利不利があると言われています。
次からはルールごとのスペシャルの勝率を見てみます。
まずは普段から感じているヤグラでのナイス玉の強さ。ガチヤグラではヤグラにイカが乗ってカウントを進めなければいけないため、ヤグラを中心にダメージフィールドを展開できるナイス玉は強制的に敵のカウントを止めたり、味方のヤグラを進めたりできるため、非常に強力だと言われていますが実際どうでしょうか。
ルール別/スペシャル別の勝率を見てみます。
win_rate_special_mode_df = pd.DataFrame()
for mode in train['mode'].unique():
win_rate_special_df = []
train_mode = train[train['mode'] == mode]
for special in buki_data.special.unique():
rate, count = win_rate_special(special, train_mode)
win_rate_special_df.append([special, rate, count])
win_rate_special_df = pd.DataFrame(win_rate_special_df, columns=[mode + '_special', mode + '_win_rate', mode + '_count'])
win_rate_special_df = win_rate_special_df.sort_values(by= mode + '_win_rate', ascending=False)
win_rate_special_df = win_rate_special_df.reset_index(drop=True)
win_rate_special_mode_df = pd.concat([win_rate_special_mode_df, win_rate_special_df], axis=1)
win_rate_special_mode_df
nawabari_special | nawabari_win_rate | nawabari_count | hoko_special | hoko_win_rate | hoko_count | yagura_special | yagura_win_rate | yagura_count | area_special | area_win_rate | area_count | asari_special | asari_win_rate | asari_count | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | quickbomb_pitcher | 0.531161 | 706 | quickbomb_pitcher | 0.553648 | 699 | nicedama | 0.514402 | 8610 | ultrahanko | 0.523176 | 2481 | quickbomb_pitcher | 0.516539 | 393 |
1 | ultrahanko | 0.520316 | 2781 | splashbomb_pitcher | 0.531447 | 636 | splashbomb_pitcher | 0.511401 | 614 | sphere | 0.514141 | 8274 | armor | 0.516531 | 7471 |
2 | nicedama | 0.517459 | 5871 | ultrahanko | 0.517877 | 3580 | chakuchi | 0.510882 | 12314 | chakuchi | 0.508521 | 12147 | robotbomb_pitcher | 0.511111 | 765 |
3 | kyubanbomb_pitcher | 0.510399 | 4087 | armor | 0.503935 | 9656 | armor | 0.508200 | 9756 | nicedama | 0.507757 | 9089 | nicedama | 0.502402 | 5828 |
4 | curlingbomb_pitcher | 0.506839 | 2705 | chakuchi | 0.503067 | 13859 | amefurashi | 0.498551 | 11042 | splashbomb_pitcher | 0.502890 | 865 | sphere | 0.501260 | 6350 |
5 | jetpack | 0.504976 | 5527 | kyubanbomb_pitcher | 0.499380 | 3228 | jetpack | 0.497645 | 6583 | armor | 0.501975 | 12154 | amefurashi | 0.498797 | 8316 |
6 | armor | 0.499940 | 8287 | amefurashi | 0.498949 | 10943 | bubble | 0.494716 | 3028 | amefurashi | 0.499154 | 14182 | ultrahanko | 0.498505 | 2341 |
7 | missile | 0.498084 | 12528 | sphere | 0.497786 | 5420 | missile | 0.493032 | 11338 | kyubanbomb_pitcher | 0.494451 | 5226 | jetpack | 0.497728 | 5061 |
8 | amefurashi | 0.497704 | 12411 | robotbomb_pitcher | 0.497630 | 1055 | sphere | 0.491856 | 5894 | bubble | 0.492367 | 4389 | chakuchi | 0.496289 | 10508 |
9 | chakuchi | 0.496397 | 13739 | nicedama | 0.497526 | 7276 | robotbomb_pitcher | 0.491318 | 979 | curlingbomb_pitcher | 0.492325 | 912 | missile | 0.492769 | 8505 |
10 | sphere | 0.486922 | 8258 | jetpack | 0.495804 | 7864 | ultrahanko | 0.488470 | 1431 | quickbomb_pitcher | 0.486996 | 1115 | kyubanbomb_pitcher | 0.476653 | 1649 |
11 | robotbomb_pitcher | 0.479284 | 1062 | missile | 0.493491 | 10831 | quickbomb_pitcher | 0.479508 | 488 | jetpack | 0.486606 | 6570 | curlingbomb_pitcher | 0.475638 | 431 |
12 | bubble | 0.470917 | 3576 | bubble | 0.488334 | 3086 | presser | 0.470302 | 5556 | missile | 0.485855 | 10569 | bubble | 0.475108 | 3696 |
13 | splashbomb_pitcher | 0.470748 | 735 | presser | 0.474390 | 5291 | kyubanbomb_pitcher | 0.463152 | 1859 | presser | 0.437034 | 2414 | splashbomb_pitcher | 0.471579 | 475 |
14 | presser | 0.422812 | 5234 | curlingbomb_pitcher | 0.457413 | 634 | curlingbomb_pitcher | 0.453216 | 342 | robotbomb_pitcher | 0.420534 | 711 | presser | 0.465357 | 1934 |
nawabari_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['nawabari_special'] == 'nicedama']['nawabari_win_rate'].values[0]
area_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['area_special'] == 'nicedama']['area_win_rate'].values[0]
yagura_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['yagura_special'] == 'nicedama']['yagura_win_rate'].values[0]
hoko_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['hoko_special'] == 'nicedama']['hoko_win_rate'].values[0]
asari_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['asari_special'] == 'nicedama']['asari_win_rate'].values[0]
plt.bar(['nawabari', 'area', 'yagura', 'hoko', 'asari'],[nawabari_win_rate, area_win_rate, yagura_win_rate, hoko_win_rate, asari_win_rate])
plt.ylim(0.4, 0.6) ,plt.title('nicedama win rate');
ヤグラでの勝率1位はやはりナイス玉でした。ただ、ヤグラ、アサリは他のルールに比べてスペシャルによる勝率の違いは少ないようです。ナイス玉はナワバリやエリアでも強いことがわかります。
ウルトラハンコは発動すると巨大なハンコを地面に打ち付けながら進むスペシャルのため、ヤグラへの干渉が難しく弱いと言われています。ホコではホコバリアを一瞬で割れたり、ホコが進む道を作ったりとウルトラハンコがあると非常に助かる場面が多い印象です。
nawabari_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['nawabari_special'] == 'ultrahanko']['nawabari_win_rate'].values[0]
area_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['area_special'] == 'ultrahanko']['area_win_rate'].values[0]
yagura_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['yagura_special'] == 'ultrahanko']['yagura_win_rate'].values[0]
hoko_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['hoko_special'] == 'ultrahanko']['hoko_win_rate'].values[0]
asari_win_rate = win_rate_special_mode_df[win_rate_special_mode_df['asari_special'] == 'ultrahanko']['asari_win_rate'].values[0]
plt.bar(['nawabari', 'area', 'yagura', 'hoko', 'asari'],[nawabari_win_rate, area_win_rate, yagura_win_rate, hoko_win_rate, asari_win_rate])
plt.ylim(0.4, 0.6) ,plt.title('ultrahanko win rate');
本当にヤグラに弱かったですね。ちょっとびっくりしました。 ホコは2位でやはり強いようですが、ナワバリやエリアでも強さを見せます。
インクアーマーとは発動すると自分と味方全員にインクのアーマーをまとわせます。アーマーは30のダメージを受けると消滅します。
ガチマッチに潜ってアーマー2枚編成だとうまくアーマーが回って強いような気がしているので、検証してみます。3枚以上だと、発動の被りなどがあってイマイチな気がしていますが、どうでしょうか。
インクアーマーがある場合の平均勝率は0.506です。
def win_rate_special_count(special, special_count, df):
count = (len(df[df[special + '_A'] == special_count]) + len(df[df[special + '_B'] == special_count])) # それぞれのチームで対象スペシャルが出現する試合数のカウント
win = (len(df[(df[special + '_A'] == special_count) & (df.y == 1)]) +len((df[(df[special + '_B'] == special_count) & (df.y == 0)]))) # それぞれのチームで対象スペシャルが出現する試合のうち各チームが勝利する試合数のカウント
rate = win / count
return rate, count
win_rate_special_df = []
special = 'armor'
for armor_count in range(1, 5):
rate, count = win_rate_special_count(special, armor_count, train)
win_rate_special_df.append([special, armor_count, rate, count])
win_rate_special_df = pd.DataFrame(win_rate_special_df, columns=['special','armor_count', 'win_rate', 'count'])
win_rate_special_df = win_rate_special_df.sort_values(by= 'win_rate', ascending=False)
win_rate_special_df = win_rate_special_df.reset_index(drop=True)
win_rate_special_df
special | armor_count | win_rate | count | |
---|---|---|---|---|
0 | armor | 2 | 0.517258 | 6403 |
1 | armor | 1 | 0.504119 | 40542 |
2 | armor | 3 | 0.477089 | 371 |
3 | armor | 4 | 0.000000 | 8 |
仮説通りインクアーマー二枚が最も勝率が高いことがわかりました。
見晴らしが良かったり、高台が存在するステージ(アロワナモール、デボン海洋博物館、ホテルニューオートロ、モンガラキャンプ場など)では長射程が有利だと言われていますが、本当でしょうか。 今回はチャージャー種、スピナー種を長射程とします。(ジェットスイーパーも長いけど若干役割が違いそうので今回は除外)
cols = ['A1-weapon', 'A2-weapon', 'A3-weapon', 'A4-weapon', 'B1-weapon', 'B2-weapon', 'B3-weapon', 'B4-weapon']
for col in cols:
train = train.merge(buki_data[['key','category2']], left_on=col, right_on='key', how='left').drop(columns='key').rename(columns={'category2':'category2' + '_' + col})
from collections import Counter
def category2_count(df):
#df_A = df[['category2_A1-weapon', 'category2_A2-weapon', 'category2_A3-weapon', 'category2_A4-weapon']].values
df_A = df[['category2_A2-weapon', 'category2_A3-weapon', 'category2_A4-weapon']].values
df_B = df[['category2_B1-weapon', 'category2_B2-weapon', 'category2_B3-weapon', 'category2_B4-weapon']].values
A_category2 = pd.DataFrame([Counter(x) for x in df_A]).fillna(0).astype('int8')
B_category2 = pd.DataFrame([Counter(x) for x in df_B]).fillna(0).astype('int8')
A_category2 = A_category2.add_suffix('_A')
B_category2 = B_category2.add_suffix('_B')
X = pd.concat([A_category2, B_category2], axis=1)
return X
X = category2_count(train)
train = pd.concat([train, X], axis=1)
def win_rate_category2(category2, df):
count = (len(df[df[category2 + '_A'] != 0]) + len(df[df[category2 + '_B'] != 0])) # それぞれのチームで対象カテゴリーが出現する試合数のカウント
win = (len(df[(df[category2 + '_A'] != 0) & (df.y == 1)]) +len((df[(df[category2 + '_B'] != 0) & (df.y == 0)]))) # それぞれのチームで対象カテゴリーが出現する試合のうち各チームが勝利する試合数のカウント
rate = win / count
return rate, count
win_rate_category2_df = []
for category2 in buki_data.category2.unique():
rate, count = win_rate_category2(category2, train)
win_rate_category2_df.append([category2, rate, count])
win_rate_category2_df = pd.DataFrame(win_rate_category2_df, columns=['category2', 'win_rate', 'count'])
win_rate_category2_df = win_rate_category2_df.sort_values(by= 'win_rate', ascending=False)
win_rate_category2_df = win_rate_category2_df.reset_index(drop=True)
win_rate_category2_df
category2 | win_rate | count | |
---|---|---|---|
0 | reelgun | 0.514023 | 14262 |
1 | brella | 0.506455 | 9450 |
2 | roller | 0.504982 | 45861 |
3 | maneuver_cat | 0.502059 | 53922 |
4 | shooter | 0.501645 | 110321 |
5 | slosher | 0.501004 | 31862 |
6 | splatling | 0.499769 | 32523 |
7 | brush | 0.497998 | 16237 |
8 | blaster | 0.481241 | 24069 |
9 | charger | 0.464298 | 43597 |
チャージャー種の平均勝率は0.464、スピナー種の勝率は0.500
win_rate_charger_df = []
category2 = 'charger'
print('Charger win rate by stage\n')
for stage in train.stage.unique():
df = train[train['stage'] == stage]
rate, count = win_rate_category2(category2, df)
win_rate_charger_df.append([stage, rate, count])
win_rate_charger_df = pd.DataFrame(win_rate_charger_df, columns=['category2', 'win_rate', 'count'])
win_rate_charger_df = win_rate_charger_df.sort_values(by= 'win_rate', ascending=False)
win_rate_charger_df = win_rate_charger_df.reset_index(drop=True)
win_rate_charger_df
Charger win rate by stage
category2 | win_rate | count | |
---|---|---|---|
0 | devon | 0.476166 | 1909 |
1 | tachiuo | 0.475890 | 1742 |
2 | kombu | 0.474661 | 1993 |
3 | mongara | 0.472431 | 1596 |
4 | mutsugoro | 0.470641 | 2214 |
5 | fujitsubo | 0.470130 | 1540 |
6 | zatou | 0.469608 | 2221 |
7 | bbass | 0.467863 | 1727 |
8 | ama | 0.466968 | 2437 |
9 | otoro | 0.466296 | 1439 |
10 | chozame | 0.465444 | 2344 |
11 | mozuku | 0.464846 | 1721 |
12 | sumeshi | 0.464234 | 2055 |
13 | anchovy | 0.463684 | 1721 |
14 | hakofugu | 0.462973 | 1877 |
15 | ajifry | 0.461787 | 1858 |
16 | hokke | 0.461449 | 1725 |
17 | arowana | 0.459330 | 1881 |
18 | engawa | 0.459104 | 1675 |
19 | shottsuru | 0.457776 | 2167 |
20 | manta | 0.455037 | 2035 |
21 | gangaze | 0.448685 | 1939 |
22 | battera | 0.444133 | 1781 |
win_rate_charger_df = []
category2 = 'splatling'
print('Splatling win rate by stage\n')
for stage in train.stage.unique():
df = train[train['stage'] == stage]
rate, count = win_rate_category2(category2, df)
win_rate_charger_df.append([stage, rate, count])
win_rate_charger_df = pd.DataFrame(win_rate_charger_df, columns=['category2', 'win_rate', 'count'])
win_rate_charger_df = win_rate_charger_df.sort_values(by= 'win_rate', ascending=False)
win_rate_charger_df = win_rate_charger_df.reset_index(drop=True)
win_rate_charger_df
Splatling win rate by stage
category2 | win_rate | count | |
---|---|---|---|
0 | hakofugu | 0.534091 | 1408 |
1 | otoro | 0.525424 | 1062 |
2 | sumeshi | 0.523605 | 1631 |
3 | mozuku | 0.523343 | 1328 |
4 | ajifry | 0.516641 | 1322 |
5 | devon | 0.514474 | 1520 |
6 | arowana | 0.513928 | 1436 |
7 | mongara | 0.512007 | 1166 |
8 | hokke | 0.507614 | 1379 |
9 | engawa | 0.503912 | 1278 |
10 | chozame | 0.502065 | 1695 |
11 | fujitsubo | 0.499574 | 1173 |
12 | ama | 0.490670 | 1822 |
13 | manta | 0.490148 | 1624 |
14 | bbass | 0.489960 | 1245 |
15 | gangaze | 0.488945 | 1583 |
16 | mutsugoro | 0.487805 | 1681 |
17 | anchovy | 0.487766 | 1267 |
18 | shottsuru | 0.484772 | 1576 |
19 | battera | 0.479937 | 1271 |
20 | kombu | 0.478723 | 1504 |
21 | zatou | 0.476506 | 1511 |
22 | tachiuo | 0.468780 | 1041 |
チャージャー種の勝率が高いのはデボン海洋博物館、タチウオパーキング、コンブトラック、モンガラキャンプ場、ホテルニューオートロ、 スピナー種の勝率が高いのはハコフグ倉庫、ホテルニューオートロ、スメーシーワールド、モズク農園、アジフライスタジアムとなりました。 筆者の経験則とは少しズレますが、ある程度納得のいく結果のようにも見えます。チャージャーでは平均と比較して勝率の高いタチウオパーキングが、スピナーでは最も勝率が低くなっているのが面白いです。長射程といってもステージごとの勝ちやすさの違いがあるようです。筆者はどちらも使ったことがないのでよくわかりません。
スピナー種はチャージャー種に比べて、ステージごとの勝率の差が大きいのも面白い発見だと思います。 スピナー種 : 0.468~0.534 チャージャー種: 0.444~0.476
他のブキもステージごとの勝率を見てみるといろいろ面白いと思います。
筆者は自分のチームにチャージャー二枚がくると負けることが多いような気がしています。実際のところどうなのか検証してみます。
def win_rate_category2_count(category2, category2_count, df):
count = (len(df[df[category2 + '_A'] == category2_count]) + len(df[df[category2 + '_B'] == category2_count])) # それぞれのチームで対象カテゴリーが出現する試合数のカウント
win = (len(df[(df[category2 + '_A'] == category2_count) & (df.y == 1)]) +len((df[(df[category2 + '_B'] == category2_count) & (df.y == 0)]))) # それぞれのチームで対象カテゴリーが出現する試合のうち各チームが勝利する試合数のカウント
rate = win / count
return rate, count
win_rate_category2_df = []
category2 = 'charger'
for category2_count in range(1, 5):
rate, count = win_rate_category2_count(category2, category2_count, train)
win_rate_category2_df.append([category2, category2_count, rate, count])
win_rate_category2_df = pd.DataFrame(win_rate_category2_df, columns=['category2','category2_count', 'win_rate', 'count'])
win_rate_category2_df = win_rate_category2_df.sort_values(by= 'win_rate', ascending=False)
win_rate_category2_df = win_rate_category2_df.reset_index(drop=True)
win_rate_category2_df
category2 | category2_count | win_rate | count | |
---|---|---|---|---|
0 | charger | 1 | 0.471333 | 39819 |
1 | charger | 2 | 0.397853 | 3539 |
2 | charger | 3 | 0.280851 | 235 |
3 | charger | 4 | 0.000000 | 4 |
仮説通りチャージャー二枚編成は勝率が低いようです。
ルールやステージにより勝ちやすいブキやスペシャルは確かに存在しそうです。 今回やらなかったこととして、ブキの組み合わせやウデマエ(C-~X)別でのブキの勝率の違いなども参考になるかもしれません。
pop-ketle
スプラはやったことがなくてドメイン知識がなく、どこからとっかかりをつかんだものかなと悩んでいたのですごく参考になりました!ありがとうございます。
wisp
めちゃめちゃためになりました!ありがとうございます!