League of Legend RiotAPIでデータサイエンスを勉強しよう【その2】

前回の記事で手に入れた、各試合のデータ(68試合分)から、勝敗にはどんな要因が関係しているのかを探ります。

APIで得られたデータをCSV

前回、APIを繰り返し使用することで、ディクショナリのリストとしてデータを取得しました。 これをcsvファイルにして、保存しておきましょう。 ここでは、よくデータ分析に使われているpandasモジュールを使ってcsvファイルとして出力してます。

import pandas as pd

#statsListというリストにLoLの試合データを前回保存しました。
#これをpandasのデータフレーム型に変更します。
stats_df = pd.DataFrame(statsList)

#stats_dfの中でもあまり試合に関係なさそうなカラム(例えばダブルキルの数とか)をdrop()によってそぎ落としています。
df = stats_df.drop(["perk0",
"perk0Var1",
"perk0Var2",
"perk0Var3",
"perk1",
"perk1Var1",
"perk1Var2",
"perk1Var3",
"perk2",
"perk2Var1",
"perk2Var2",
"perk2Var3",
"perk3",
"perk3Var1",
"perk3Var2",
"perk3Var3",
"perk4",
"perk4Var1",
"perk4Var2",
"perk4Var3",
"perk5",
"perk5Var1",
"perk5Var2",
"perk5Var3",
"perkPrimaryStyle",
"perkSubStyle",
"combatPlayerScore",
"doubleKills",
"firstBloodAssist",
"firstBloodKill",
"firstInhibitorAssist",
"firstInhibitorKill",
"firstTowerAssist",
"firstTowerKill",
"inhibitorKills",
"item0",
"item1",
"item2",
"item3",
"item4",
"item5",
"item6",
"killingSprees",
"largestCriticalStrike",
"largestKillingSpree",
"largestMultiKill",
"neutralMinionsKilled",
"neutralMinionsKilledEnemyJungle",
"neutralMinionsKilledTeamJungle",
"objectivePlayerScore",
"participantId",
"pentaKills",
"playerScore0",
"playerScore1",
"playerScore2",
"playerScore3",
"playerScore4",
"playerScore5",
"playerScore6",
"playerScore7",
"playerScore8",
"playerScore9",
"quadraKills",
"timeCCingOthers",
"totalHeal",
"totalPlayerScore",
"totalScoreRank",
"totalTimeCrowdControlDealt",
"totalUnitsHealed",
"tripleKills",
"trueDamageDealt",
"trueDamageDealtToChampions",
"trueDamageTaken",
"turretKills",
"unrealKills",
"wardsKilled",
"sightWardsBoughtInGame"
], axis=1)

#スリムになったデータをCSVファイルとして保存します。
df.to_csv(path_or_buf = 'LoL.csv', index = False)

これで、jupyternotebookを開くたびにAPIを使う必要がなくなりました。

データをpandasで見てみよう

新しくjupyternotebookを開いて、さっき保存したデータを読み込んでみましょう。

import pandas as pd
from pandas import Series, DataFrame
import numpy as np
import matplotlib.pyplot as plt
#seabornは簡単にきれいなグラフを書くためのモジュールです。
import seaborn as sns
%matplotlib inline

#ファイルを読み込み、データフレームを生成します。
lol_df = pd.read_csv('LoL.csv')

#データフレームのwinデータをカウントプロットで見てみます。
sns.countplot('win',data = lol_df)

f:id:Sorivid:20180221223341p:plain
勝敗の数
こんな感じで勝敗のデータが簡単にプロットできます。

データ解析「相関を見てみよう」

勝敗と関係ありそうなデータは、KDAや、与えたダメージ、オブジェクトへのダメージなどでしょうか。これを探るため、相関を見てみましょう。
相関とはデータ間の関連の強さを示す数値です。 相関係数は-1から1までの実数をとります。例えばassistの数と勝敗の相関をとったとき、
1, 相関係数が1に近ければ、assist数の多い試合で勝利していることになります。
2, 相関係数が-1に近ければ、assist数が少ない試合で勝利していることになります。
3, 相関係数の絶対値が0に近ければ、assist数と勝敗には関連性が無いといえます。

#Winを1、Loseを0とするように関数を作ります。
def f(data):
    if data == True:
        return 1
    else:
        return 0

#データフレームのwinに対して上記の関数をapply()して、あらたなnumWinという列を追加します。
lol_df['numWin'] = lol_df['win'].apply(f)

#データフレーム内の全データ間を総当り的に相関係数を求めます。
#corr()とするだけですので簡単です。
corr_df = lol_df.corr()
#corr_dfのwinの列を見てみます。
corr_df.win
assists                           0.330392
champLevel                        0.150457
damageDealtToObjectives           0.570940
damageDealtToTurrets              0.583484
damageSelfMitigated              -0.243319
deaths                           -0.450515
goldEarned                        0.290285
goldSpent                         0.137464
kills                             0.224459
longestTimeSpentLiving            0.203687
magicDamageDealt                  0.102760
magicDamageDealtToChampions       0.130534
magicalDamageTaken               -0.278148
physicalDamageDealt               0.083411
physicalDamageDealtToChampions    0.018560
physicalDamageTaken              -0.174446
totalDamageDealt                  0.091768
totalDamageDealtToChampions       0.041253
totalDamageTaken                 -0.297823
totalMinionsKilled                0.074603
visionScore                       0.187652
visionWardsBoughtInGame           0.082334
wardsPlaced                       0.115123
win                               1.000000
numWin                            1.000000

勝敗と、その他データとの相関係数を見ることができました。
例えば、deathsとの相関は-0.450515ですから、physicalDamageTakenの-0.174446よりも、-1に近いです。これは、後者に比べてdeathsの少ない試合の方が勝敗に影響しているということです。
winとwinとの相関係数が1なのはあたりまえです。同じ数値なのですから。numWinも同様。

正直相関高いデータがない!

しいていうなら0.5くらいあるdamageDealtToObjectivesとdamageDealtToTurrets 、-0.5くらいのdeathsが有効なデータなのでしょうか。

ヒートマップでもうちょいみやすくしてみましょう。まぁ、この場合あまり見やすくはないんですが。

sns.heatmap(corr_df)

f:id:Sorivid:20180221223346p:plain
ヒートマップ
右端もしくは下端のデータがwinとそれぞれのデータとの相関係数です。1に近いほど赤く、-1に近いほど青く、0に近いほど白い。

マップの左上当たりに目を向けると、goldSpentとgoldEarnedとの相関は、色が濃くなっていますね。 似たような数値ですし当たり前ですね。

そのほかにchampLevelとgoldSpentの相関なんかも高いですね。これもイメージがわきますね。

このデータから何がわかるのか

さて、ではこのデータから何がわかるか。
相関係数から判断すると、オブジェクトにダメージをあたえることを優先してkillされなければ、勝率はあがる!しかしそういうわけではない!!

これはあくまで相関で、killされてない試合が勝率高いのかもしれませんが、killされてないから勝ててるとは言えません。勝ててるからkillされないのかもしれません。相関は因果関係ではありません。
オブジェクトダメージだって、そりゃ勝った試合はネクサスまで壊してるんだから、負けた試合に比べてダメージ量多いのは当たり前ですよね。

正直何もわかりませんでした。

結局これといって結論を出せませんでした。なるべくdeathを抑えるべきだということは何となくわかりましたが、あたりまえですよねそんなの。

lolがオブジェクトを奪うゲームだという裏付けには少し加担できているかもしれません。相手をkillするゲームではないってうまいプレイヤーさんは言いますよね。

今後どう進めていくか

さて、難しいですね。勝利の仕組みを解き明かす方法はないのでしょうか。 てか、ワードが全く相関ないのはびっくり。ある程度の数置かないと勝敗にかかわってこないのかな。それとも毎回同じくらいのワードしか置けてないってことなのかな。

改善点としては、
1, CSを見てないこと。
2, もっと時系列データに注目すること

こんくらいしか今思いついてません。 こんなデータに注目してみたら?とかあったらぜひコメントください。

読んでいただき、ありがとうございました。