対戦ゲームデータ分析甲子園

目指せ"Another" バトル優勝!

賞金: 100,000 参加ユーザー数: 605 約4年前に終了

スプラコンペ 勝率検証EDA

スプラトゥーン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)

【検証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】ルールによって有利なブキがある??

スプラトゥーン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割はヤバいですね。一方のチームにしか含まれない場合などを調べるともっと高いかもしれません。 他のルールは上位に来ているもののサンプル数が少なく、ブキ以外の要因(特定のプレイヤースキルなど)に左右されている可能性があるように思えます。

【検証3】強いスペシャルはある??

インクを一定量塗ると、スペシャルゲージがたまり、スペシャルと呼ばれる特別な攻撃を放つことができます。単体で相手のイカをキルできるものや、メインブキの攻撃を補助するもの、味方の防御力を高めるものなど様々なものがあります。
それぞれルールごとに強いものや弱いものがあると言われています。

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ブキのみしかこのスペシャルを使えるブキはありません。非常にキル力の高いスペシャルと言われています。最下位はハイパープレッサーとなりました。ハイパープレッサーはスペシャルの性質上、後衛ブキが持つことが多く、チャージャーやジェットスイーパーカスタムの勝率の低さを反映していると思われます。

【検証4】ヤグラのナイス玉強すぎない??

それぞれのスペシャルの性質上、ルールごとに有利不利があると言われています。 次からはルールごとのスペシャルの勝率を見てみます。 まずは普段から感じているヤグラでのナイス玉の強さ。ガチヤグラではヤグラにイカが乗ってカウントを進めなければいけないため、ヤグラを中心にダメージフィールドを展開できるナイス玉は強制的に敵のカウントを止めたり、味方のヤグラを進めたりできるため、非常に強力だと言われていますが実際どうでしょうか。
ルール別/スペシャル別の勝率を見てみます。

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位はやはりナイス玉でした。ただ、ヤグラ、アサリは他のルールに比べてスペシャルによる勝率の違いは少ないようです。ナイス玉はナワバリやエリアでも強いことがわかります。

【検証5】ウルトラハンコはホコに強く、ヤグラに弱い??

ウルトラハンコは発動すると巨大なハンコを地面に打ち付けながら進むスペシャルのため、ヤグラへの干渉が難しく弱いと言われています。ホコではホコバリアを一瞬で割れたり、ホコが進む道を作ったりとウルトラハンコがあると非常に助かる場面が多い印象です。

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位でやはり強いようですが、ナワバリやエリアでも強さを見せます。

【検証6】 インクアーマー二枚編成は強い??

インクアーマーとは発動すると自分と味方全員にインクのアーマーをまとわせます。アーマーは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

仮説通りインクアーマー二枚が最も勝率が高いことがわかりました。

【検証7】 長射程有利のステージがあるって本当??

見晴らしが良かったり、高台が存在するステージ(アロワナモール、デボン海洋博物館、ホテルニューオートロ、モンガラキャンプ場など)では長射程が有利だと言われていますが、本当でしょうか。 今回はチャージャー種、スピナー種を長射程とします。(ジェットスイーパーも長いけど若干役割が違いそうので今回は除外)

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

他のブキもステージごとの勝率を見てみるといろいろ面白いと思います。

【検証8】 チャージャー二枚編成は弱い??

筆者は自分のチームにチャージャー二枚がくると負けることが多いような気がしています。実際のところどうなのか検証してみます。

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)別でのブキの勝率の違いなども参考になるかもしれません。

添付データ

  • spla_EDA.ipynb?X-Amz-Expires=10800&X-Amz-Date=20241121T103708Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • spla_EDA.ipynb?X-Amz-Expires=10800&X-Amz-Date=20241121T103708Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • Aws4 request&x amz signedheaders=host&x amz signature=408a6143e2956f1e76da91a337bc3ea8746e64bce7cc5d0c0ef76753343e865e
    pop-ketle

    スプラはやったことがなくてドメイン知識がなく、どこからとっかかりをつかんだものかなと悩んでいたのですごく参考になりました!ありがとうございます。

    Icon2
    wisp

    めちゃめちゃためになりました!ありがとうございます!

    Favicon
    new user
    コメントするには 新規登録 もしくは ログイン が必要です。