[OPGG] 롤 진영별 승률 확인하기

Updated:

1. 블루/레드 승률

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import pymysql
import sys
import os
# # OP.GG Database에 접근하기 위한 연결 객체 생성
# con = pymysql.connect(
#     user = os.environ['LOL_KR_ID'],
#     passwd = os.environ['LOL_KR_PW'],
#     host = os.environ['LOL_KR_HOST'],
#     db = 'lol',
#     charset = 'utf8'
# )
# cursor = con.cursor(pymysql.cursors.DictCursor)
# # 데이터 구조 파악을 위한 sample data fetch
# # SQL
# cursor.execute('''
# SELECT STRAIGHT_JOIN *
# FROM opGame o FORCE INDEX (ix_createDate),
# p_opGameStats p FORCE INDEX (`PRIMARY`)
# WHERE o.gameId = p.gameId
# AND o.createDate >= '2021-08-12'
# AND p.createDate >= '2021-08-12'
# ORDER BY o.createDate DESC
# LIMIT 30
# ''')
# sample = cursor.fetchall()

# # DataFrame으로 변환
# sample = pd.DataFrame(sample)
# sample.to_csv("sample.csv", mode='w')
# 미리 뽑아둔 CSV 파일에서 가져오기
sample = pd.read_csv('Day02_01_sample.csv').drop("Unnamed: 0", axis=1)
  • 원래는 pymysql 패키지를 통해 OPGG 데이터베이스에서 자료를 불러온다.

  • 나는 접근 권한이 없으므로 미리 만든 CSV파일을 제공받아 사용하였다.

# 데이터 확인
sample.head()
gameId gameMapId gameType subType createDate gameLength interestScore goldLeadChangeCount isSurrender coreMinute ... tierRank position isDetail isRanked perk0 perkPrimaryStyle perkSubStyle opScore opScoreRank isOPScoreMaxInTeam
0 5386357435 11 MATCHED_GAME 420 2021-08-12 15:53:21 1859 0 0 NaN NaN ... S220 T 1 1 8229 8200 8000 5.17 10 0
1 5386357435 11 MATCHED_GAME 420 2021-08-12 15:53:21 1859 0 0 NaN NaN ... S271 S 1 1 8437 8400 8000 6.32 6 1
2 5386357435 11 MATCHED_GAME 420 2021-08-12 15:53:21 1859 0 0 NaN NaN ... S247 M 1 1 8214 8200 8300 8.14 1 1
3 5386357435 11 MATCHED_GAME 420 2021-08-12 15:53:21 1859 0 0 NaN NaN ... S222 A 1 1 8128 8100 8000 5.44 7 0
4 5386357435 11 MATCHED_GAME 420 2021-08-12 15:53:21 1859 0 0 NaN NaN ... S216 A 1 1 8229 8200 8300 6.85 4 0

5 rows × 87 columns

# columns 확인
print(sample.columns)
Index(['gameId', 'gameMapId', 'gameType', 'subType', 'createDate',
       'gameLength', 'interestScore', 'goldLeadChangeCount', 'isSurrender',
       'coreMinute', 'coreMinuteGoldDiff', 'isComebackWin',
       'maxGoldDiffBeforeComeback', 'maxGoldDiffMinBeforeComeback',
       'firstGoldLeadMinAfterComeback', 'version', 'scanned', 'isFullData',
       'isPlusData', 'p.gameId', 'p.createDate', 'teamId', 'summonerId',
       'isUnknownSummoner', 'participantId', 'championId', 'result',
       'skinIndex', 'spell1', 'spell2', 'leaver', 'experienceEarned',
       'eligibleFirstWinOfDay', 'ipEarned', 'boostXpEarned', 'boostIpEarned',
       'premadeSize', 'item0', 'item1', 'item2', 'item3', 'item4', 'item5',
       'item6', 'level', 'championsKilled', 'numDeaths', 'assists',
       'neutralMinionsKilled', 'turretsKilled', 'barracksKilled',
       'minionsKilled', 'largestMultiKill', 'largestCriticalStrike',
       'largestKillingSpree', 'goldEarned', 'physicalDamageDealtToChampions',
       'magicDamageDealtPlayer', 'physicalDamageTaken',
       'sightWardsBoughtInGame', 'visionWardsBoughtInGame', 'wardKilled',
       'wardPlaced', 'totalHeal', 'totalDamageDealtToChampions',
       'totalDamageDealt', 'totalDamageTaken',
       'neutralMinionsKilledEnemyJungle', 'neutralMinionsKilledTeamJungle',
       'visionScore', 'timeCCingOthers', 'damageSelfMitigated',
       'damageDealtToObjectives', 'damageDealtToTurrets', 'lane', 'role',
       'keystoneMasteryId', 'tierRank', 'position', 'isDetail', 'isRanked',
       'perk0', 'perkPrimaryStyle', 'perkSubStyle', 'opScore', 'opScoreRank',
       'isOPScoreMaxInTeam'],
      dtype='object')
# Column 수가 많아서 print했을 때 다 보이게 옵션 설정
pd.set_option('display.max_columns', None)

# 값이 정상적인지 보기 위해 describe로 요약 정보 출력
sample.describe(include='all',datetime_is_numeric=True)
gameId gameMapId gameType subType createDate gameLength interestScore goldLeadChangeCount isSurrender coreMinute coreMinuteGoldDiff isComebackWin maxGoldDiffBeforeComeback maxGoldDiffMinBeforeComeback firstGoldLeadMinAfterComeback version scanned isFullData isPlusData p.gameId p.createDate teamId summonerId isUnknownSummoner participantId championId result skinIndex spell1 spell2 leaver experienceEarned eligibleFirstWinOfDay ipEarned boostXpEarned boostIpEarned premadeSize item0 item1 item2 item3 item4 item5 item6 level championsKilled numDeaths assists neutralMinionsKilled turretsKilled barracksKilled minionsKilled largestMultiKill largestCriticalStrike largestKillingSpree goldEarned physicalDamageDealtToChampions magicDamageDealtPlayer physicalDamageTaken sightWardsBoughtInGame visionWardsBoughtInGame wardKilled wardPlaced totalHeal totalDamageDealtToChampions totalDamageDealt totalDamageTaken neutralMinionsKilledEnemyJungle neutralMinionsKilledTeamJungle visionScore timeCCingOthers damageSelfMitigated damageDealtToObjectives damageDealtToTurrets lane role keystoneMasteryId tierRank position isDetail isRanked perk0 perkPrimaryStyle perkSubStyle opScore opScoreRank isOPScoreMaxInTeam
count 3.000000e+01 30.000000 30 30.000000 30 30.000000 30.0 30.0 0.0 0.0 0.0 30.0 0.0 0.0 0.0 30 30.0 30.0 30.0 3.000000e+01 30 30.000000 3.000000e+01 30.0 30.000000 30.000000 30 30.0 30.000000 30.000000 30.0 30.0 30.0 30.0 30.0 30.0 30.0 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.0 30.000000 30.00000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30 30 30.0 21 20 30.0 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000 30.000000
unique NaN NaN 1 NaN 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN 3 NaN NaN NaN NaN NaN 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 5 5 NaN 21 5 NaN NaN NaN NaN NaN NaN NaN NaN
top NaN NaN MATCHED_GAME NaN 2021-08-12 15:53:21 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 11.16.390.1945 NaN NaN NaN NaN 2021-08-12 15:53:21 NaN NaN NaN NaN NaN LOSE NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NONE DUO_SUPPOR NaN S220 T NaN NaN NaN NaN NaN NaN NaN NaN
freq NaN NaN 30 NaN 10 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 30 NaN NaN NaN NaN 10 NaN NaN NaN NaN NaN 15 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 10 13 NaN 1 4 NaN NaN NaN NaN NaN NaN NaN NaN
mean 5.386362e+09 11.333333 NaN 433.333333 NaN 1451.333333 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 1.0 0.0 5.386362e+09 NaN 150.000000 4.941984e+07 0.0 5.500000 149.333333 NaN 0.0 12.733333 7.233333 0.0 0.0 0.0 0.0 0.0 0.0 0.0 4478.066667 3267.900000 3547.700000 3154.466667 2350.333333 1703.700000 2850.966667 15.000000 7.200000 7.200000 12.033333 19.200000 0.700000 0.133333 86.766667 1.666667 159.866667 2.600000 11354.100000 8714.566667 9815.466667 12254.333333 0.0 1.266667 1.60000 6.433333 6539.466667 19219.766667 95026.933333 23116.166667 3.300000 11.033333 14.066667 20.333333 17038.466667 6389.700000 1977.466667 NaN NaN 0.0 NaN NaN 1.0 0.333333 8124.466667 8106.666667 8183.333333 4.248000 3.666667 0.133333
std 2.159648e+04 0.479463 NaN 12.685407 NaN 321.383146 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 0.0 0.0 2.159648e+04 NaN 50.854763 3.312891e+07 0.0 2.921384 141.786224 NaN 0.0 8.885996 8.605064 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1946.282078 1716.157041 1799.336221 1379.155932 1724.201371 1689.720343 807.671898 1.982684 4.029204 2.696102 8.015000 40.958684 1.087547 0.345746 69.981122 0.758098 382.097364 1.693802 2591.659878 8109.920650 9675.071080 5854.532181 0.0 1.552158 2.19089 7.242991 6187.051867 6806.726332 61450.746398 8060.104013 8.363096 26.110486 14.843862 17.070510 11376.787213 10922.650235 2687.352264 NaN NaN 0.0 NaN NaN 0.0 0.479463 137.492503 128.474694 134.121235 3.182913 3.555795 0.345746
min 5.386339e+09 11.000000 NaN 420.000000 NaN 1089.000000 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 1.0 0.0 5.386339e+09 NaN 100.000000 1.228419e+06 0.0 1.000000 3.000000 NaN 0.0 1.000000 3.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1054.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 11.000000 1.000000 3.000000 2.000000 0.000000 0.000000 0.000000 5.000000 1.000000 0.000000 0.000000 6046.000000 260.000000 0.000000 3156.000000 0.0 0.000000 0.00000 0.000000 329.000000 6659.000000 10532.000000 7719.000000 0.000000 0.000000 0.000000 0.000000 3181.000000 0.000000 0.000000 NaN NaN 0.0 NaN NaN 1.0 0.000000 8005.000000 8000.000000 8000.000000 0.000000 0.000000 0.000000
25% 5.386339e+09 11.000000 NaN 420.000000 NaN 1089.000000 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 1.0 0.0 5.386339e+09 NaN 100.000000 1.886260e+07 0.0 3.000000 55.750000 NaN 0.0 7.000000 4.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3068.000000 2567.250000 3043.250000 3035.000000 1052.250000 0.000000 2052.000000 13.250000 4.250000 5.000000 4.500000 0.000000 0.000000 0.000000 30.750000 1.000000 0.000000 2.000000 9661.750000 1043.250000 2015.500000 7782.000000 0.0 0.000000 0.00000 0.000000 2098.250000 15237.500000 49813.000000 17870.250000 0.000000 0.000000 0.000000 9.000000 9202.250000 497.750000 294.250000 NaN NaN 0.0 NaN NaN 1.0 0.000000 8010.000000 8000.000000 8100.000000 0.000000 0.000000 0.000000
50% 5.386357e+09 11.000000 NaN 430.000000 NaN 1406.000000 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 1.0 0.0 5.386357e+09 NaN 150.000000 5.758059e+07 0.0 5.500000 87.500000 NaN 0.0 12.000000 4.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3854.000000 3092.500000 3095.000000 3105.500000 3020.000000 1036.000000 3340.000000 15.500000 6.000000 8.000000 10.000000 0.000000 0.000000 0.000000 56.000000 2.000000 0.000000 2.000000 11878.000000 7584.500000 6340.500000 11663.000000 0.0 1.000000 1.00000 5.500000 5214.500000 18970.500000 83774.000000 23526.500000 0.000000 0.000000 13.500000 18.000000 11953.000000 2839.000000 856.000000 NaN NaN 0.0 NaN NaN 1.0 0.000000 8120.000000 8100.000000 8200.000000 5.435000 3.000000 0.000000
75% 5.386390e+09 12.000000 NaN 450.000000 NaN 1859.000000 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 1.0 0.0 5.386390e+09 NaN 200.000000 8.357982e+07 0.0 8.000000 217.250000 NaN 0.0 14.000000 4.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6654.500000 3157.750000 3182.750000 3157.750000 3111.000000 3082.250000 3363.750000 16.000000 10.750000 9.000000 17.250000 8.750000 1.000000 0.000000 139.250000 2.000000 22.750000 3.000000 12981.250000 14667.250000 16026.750000 15075.750000 0.0 2.000000 2.00000 9.750000 8657.250000 24286.250000 117880.250000 28370.500000 0.750000 4.000000 21.750000 27.500000 22984.750000 8188.750000 2779.500000 NaN NaN 0.0 NaN NaN 1.0 1.000000 8214.000000 8200.000000 8300.000000 6.925000 6.750000 0.000000
max 5.386390e+09 12.000000 NaN 450.000000 NaN 1859.000000 0.0 0.0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 1.0 0.0 5.386390e+09 NaN 200.000000 8.818104e+07 0.0 10.000000 517.000000 NaN 0.0 32.000000 32.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6692.000000 6653.000000 6692.000000 6694.000000 6672.000000 6632.000000 3364.000000 18.000000 15.000000 12.000000 31.000000 153.000000 4.000000 1.000000 271.000000 4.000000 1866.000000 7.000000 16862.000000 27427.000000 36284.000000 33100.000000 0.0 6.000000 7.00000 27.000000 26173.000000 36544.000000 251762.000000 45985.000000 32.000000 103.000000 58.000000 76.000000 47384.000000 54606.000000 11389.000000 NaN NaN 0.0 NaN NaN 1.0 1.000000 8439.000000 8400.000000 8400.000000 8.140000 10.000000 1.000000
  • 우선 컬럼이 매우 많다.
# 필요한 값만 선택

# position은 opgg가 만든 ai
sample = sample[['gameId','subType','gameLength','teamId','championId','result','position']]
  • 필요한 컬럼만 선택하였다.

  • 여기서 subType은 솔랭, 자랭 등 게임 타입이며 position은 OPGG에서 딥러닝으로 만든 컬럼이라고 한다.

# 최종적으로 만들어진 sample dataframe
sample
gameId subType gameLength teamId championId result position
0 5386357435 420 1859 200 74 LOSE T
1 5386357435 420 1859 200 235 LOSE S
2 5386357435 420 1859 100 90 WIN M
3 5386357435 420 1859 200 202 LOSE A
4 5386357435 420 1859 100 115 WIN A
5 5386357435 420 1859 200 234 LOSE J
6 5386357435 420 1859 100 48 WIN J
7 5386357435 420 1859 200 517 LOSE M
8 5386357435 420 1859 100 43 WIN S
9 5386357435 420 1859 100 92 WIN T
10 5386338795 430 1406 100 126 LOSE T
11 5386338795 430 1406 100 517 LOSE M
12 5386338795 430 1406 200 85 WIN T
13 5386338795 430 1406 200 53 WIN S
14 5386338795 430 1406 200 203 WIN J
15 5386338795 430 1406 200 166 WIN M
16 5386338795 430 1406 100 64 LOSE J
17 5386338795 430 1406 200 81 WIN A
18 5386338795 430 1406 100 81 LOSE A
19 5386338795 430 1406 100 350 LOSE S
20 5386390166 450 1089 200 61 LOSE NaN
21 5386390166 450 1089 100 17 WIN NaN
22 5386390166 450 1089 100 222 WIN NaN
23 5386390166 450 1089 200 8 LOSE NaN
24 5386390166 450 1089 100 19 WIN NaN
25 5386390166 450 1089 200 54 LOSE NaN
26 5386390166 450 1089 200 236 LOSE NaN
27 5386390166 450 1089 100 3 WIN NaN
28 5386390166 450 1089 100 420 WIN NaN
29 5386390166 450 1089 200 64 LOSE NaN
  • subType 450은 칼바람이며 420은 솔랭, 430은 일반 게임이다.

  • 보면 430은 일반 게임인데 포지션 분류가 되어 있는 것을 확인 할 수 있다.

  • subType등에 대한 정보는 여기를 참고하자.

  • (API로 끌어온 데이터가 아닌 OPGG가 정제한 데이터베이스의 데이터이므로 컬럼명은 다를 수 있다.)

# Reset sample data
%reset
Once deleted, variables cannot be recovered. Proceed (y/[n])? y
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import pymysql
import sys
import os
from scipy.stats import norm
import requests
# # OP.GG Database에 접근하기 위한 연결 객체 생성
# con = pymysql.connect(
#     user = os.environ['LOL_KR_ID'],
#     passwd = os.environ['LOL_KR_PW'],
#     host = os.environ['LOL_KR_HOST'],
#     db = 'lol',
#     charset = 'utf8'
# )
# cursor = con.cursor(pymysql.cursors.DictCursor)
# # Full data fetch
# # SQL
# cursor.execute('''
# SELECT STRAIGHT_JOIN o.gameId, subType, gameLength, teamId, championId, result, position
# FROM opGame o FORCE INDEX (ix_createDate),
# p_opGameStats p FORCE INDEX (`PRIMARY`)
# WHERE o.gameId = p.gameId
# AND o.createDate >= '2021-08-12'
# AND p.createDate >= '2021-08-12'
# LIMIT 1000000                         
# ''')
# gamestats = cursor.fetchall()

# # DataFrame으로 변환
# gamestats = pd.DataFrame(gamestats)
# gamestats.to_csv("gamestats.csv", mode='w')
# 미리 뽑아둔 CSV 파일에서 가져오기
gamestats = pd.read_csv('Day02_01_gamestats.csv', index_col=0)
  • 앞서 일부 샘플로 과정을 확인했으니 이제 전체 데이터로 진행해보자.
# 데이터 확인
gamestats
gameId subType gameLength teamId championId result position
0 5385587730 440 1804 200 81 WIN A
1 5385587730 440 1804 200 107 WIN J
2 5385587730 440 1804 100 121 LOSE J
3 5385587730 440 1804 100 517 LOSE M
4 5385587730 440 1804 100 12 LOSE S
... ... ... ... ... ... ... ...
99995 5385598894 440 1977 200 222 WIN A
99996 5385598894 440 1977 200 234 WIN J
99997 5385598894 440 1977 100 81 LOSE A
99998 5385598894 440 1977 100 1 LOSE M
99999 5385598894 440 1977 100 8 LOSE T

100000 rows × 7 columns

# 데이터 타입 확인
print(gamestats.dtypes)
gameId         int64
subType        int64
gameLength     int64
teamId         int64
championId     int64
result        object
position      object
dtype: object
# 데이터 요약 정보
gamestats.describe(include='all',datetime_is_numeric=True)
gameId subType gameLength teamId championId result position
count 1.000000e+05 100000.000000 100000.000000 100000.000000 100000.000000 100000 66998
unique NaN NaN NaN NaN NaN 3 5
top NaN NaN NaN NaN NaN WIN M
freq NaN NaN NaN NaN NaN 49677 13400
mean 5.385674e+09 447.026200 1491.257480 149.990000 149.647020 NaN NaN
std 5.216472e+04 111.647298 489.932212 50.000249 178.131795 NaN NaN
min 5.385581e+09 420.000000 190.000000 100.000000 1.000000 NaN NaN
25% 5.385629e+09 420.000000 1141.000000 100.000000 43.000000 NaN NaN
50% 5.385673e+09 430.000000 1440.000000 100.000000 86.000000 NaN NaN
75% 5.385712e+09 450.000000 1826.000000 200.000000 164.000000 NaN NaN
max 5.385774e+09 2020.000000 3887.000000 200.000000 887.000000 NaN NaN
# 각 column 별로 결측치 확인
gamestats.isnull().sum()
gameId            0
subType           0
gameLength        0
teamId            0
championId        0
result            0
position      33002
dtype: int64
# 'result' column에 있는 값 확인
gamestats['result'].unique()
array(['WIN', 'LOSE', 'UNKNOWN'], dtype=object)
  • result의 UNKNOWN은 “다시하기”이다.
# 'UNKNOWN'인 case 확인
gamestats[gamestats['result'] == 'UNKNOWN']
gameId subType gameLength teamId championId result position
300 5385762506 430 196 200 134 UNKNOWN M
301 5385762506 430 196 200 51 UNKNOWN T
302 5385762506 430 196 200 23 UNKNOWN A
303 5385762506 430 196 100 157 UNKNOWN M
304 5385762506 430 196 200 12 UNKNOWN S
... ... ... ... ... ... ... ...
97396 5385773777 440 194 200 51 UNKNOWN A
97397 5385773777 440 194 200 238 UNKNOWN M
97398 5385773777 440 194 100 16 UNKNOWN S
97399 5385773777 440 194 200 59 UNKNOWN J
97400 5385773777 440 194 100 126 UNKNOWN M

670 rows × 7 columns

# 'UNKNOWN'인 case drop
gamestats = gamestats[gamestats['result'] != 'UNKNOWN']
# 'subType' 확인
gamestats['subType'].value_counts()
420     35380
450     30850
430     20030
440     10928
850       780
830       710
840       280
2020      230
2010      120
2000       22
Name: subType, dtype: int64
# 솔랭(420), 칼바람(450)만 남기기
gamestats = gamestats[gamestats['subType'].isin([420,450])]
  • 여러 종류의 게임 중 솔로 랭크와 칼바람만 남긴다.
gamestats.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 66230 entries, 10 to 99991
Data columns (total 7 columns):
 #   Column      Non-Null Count  Dtype 
---  ------      --------------  ----- 
 0   gameId      66230 non-null  int64 
 1   subType     66230 non-null  int64 
 2   gameLength  66230 non-null  int64 
 3   teamId      66230 non-null  int64 
 4   championId  66230 non-null  int64 
 5   result      66230 non-null  object
 6   position    35380 non-null  object
dtypes: int64(5), object(2)
memory usage: 4.0+ MB
# 데이터 타입 categorical로 변환
gamestats['teamId'] = pd.Categorical(gamestats.teamId)
gamestats['subType'] = pd.Categorical(gamestats.subType)
gamestats['position'] = pd.Categorical(gamestats.position)
  • 꼭 categorical로 변환하지 않아도 되지만 해당 컬럼들의 성질을 고려해 변환한다.
# 솔랭 게임 중 포지션 분류 안 된 게임 확인
gamestats['position'][gamestats['subType'] == 420].isnull().sum()
0
  • 포지션 예측에 대해 만약 플레이어가 서폿인데 미드로 가서 던지고 하면 예측이 안된다고 한다.

  • 현재 자료에선 그런 케이스는 없다.

# 포지션 분류 안 된 게임 drop
gamestats = gamestats.drop(gamestats[(gamestats['subType']==420) & (gamestats['position'].isnull())].index)
# 'WIN', 'LOSE' -> True, False로 다루기 용이하게 변환
gamestats['result'] = (gamestats['result'] == 'WIN')
# 전처리된 DataFrame에서 row case 재확인
gamestats[['teamId','subType','result']].drop_duplicates().sort_values(["subType",'result','teamId'])
teamId subType result
180 100 420 False
33 200 420 False
30 100 420 True
182 200 420 True
52 100 450 False
10 200 450 False
11 100 450 True
50 200 450 True
# subType, championId 별로 블루팀 승률, 레드팀 승률, 각 진영 게임 수 확인
def func_calc_WR(x):
        d = []
        d.append(sum(x['teamId']==100)),
        d.append(x['result'][x['teamId']==100].sum() / sum(x['teamId']==100)),
        d.append(sum(x['teamId']==200)),
        d.append(x['result'][x['teamId']==200].sum() / sum(x['teamId']==200)),
        
        return pd.Series(d, index=['BLUE_count', 'BLUE_WR', 'RED_count', 'RED_WR'])

summary = gamestats.groupby(['subType','championId'], as_index=False).apply(func_calc_WR)
# Summary 확인
summary
subType championId BLUE_count BLUE_WR RED_count RED_WR
0 420 1 34.0 0.588235 34.0 0.588235
1 420 2 20.0 0.700000 28.0 0.464286
2 420 3 91.0 0.538462 92.0 0.489130
3 420 4 66.0 0.424242 69.0 0.449275
4 420 5 154.0 0.474026 169.0 0.449704
... ... ... ... ... ... ...
307 450 555 208.0 0.480769 200.0 0.455000
308 450 777 92.0 0.500000 89.0 0.426966
309 450 875 101.0 0.623762 120.0 0.450000
310 450 876 71.0 0.591549 68.0 0.411765
311 450 887 42.0 0.523810 53.0 0.377358

312 rows × 6 columns

  • 게임 타입과 챔피언 별로 진영에 따른 판수와 승률을 추가하였다.
# 유의수준 0.005에서 검증
summary['p_value'] = (2*norm.cdf(-abs(summary.RED_WR - summary.BLUE_WR)/
                                 np.sqrt(summary.BLUE_WR*(1-summary.BLUE_WR)/summary.BLUE_count+
                                         summary.RED_WR*(1-summary.RED_WR)/summary.RED_count)))
summary['unbalanced'] = summary['p_value'] < 0.005
  • 각 진영별로 승률이 차이가 있는지 확인하기 위해 유의확률을 직접 구하였다.

  • 기준을 0.005로 잡아 가설을 검증하였다.

summary[summary['unbalanced']].sort_values('p_value')
subType championId BLUE_count BLUE_WR RED_count RED_WR p_value unbalanced
180 450 25 192.0 0.682292 199.0 0.462312 0.000006 True
169 450 14 43.0 0.744186 42.0 0.380952 0.000289 True
195 450 40 135.0 0.629630 114.0 0.412281 0.000462 True
131 420 246 49.0 0.244898 57.0 0.526316 0.001823 True
244 450 105 101.0 0.544554 96.0 0.343750 0.003771 True
# 숫자로 된 각종 Key값에 대한 Value 가져오기
champion_constant = requests.get("http://ddragon.leagueoflegends.com/cdn/11.16.1/data/ko_KR/champion.json")
# Json 파일을 DataFrame으로 변환
champion_df = pd.DataFrame(champion_constant.json()['data']).T[['key','name']]
# 변수형 문자 -> 숫자
champion_df['key'] = pd.to_numeric(champion_df['key'])
  • 1일차에서 배웠던 챔피언 정보를 불러온다.
# 매칭되는 곳으로 내용 추가하여 새로운 DataFrame으로 저장
summary = pd.merge(summary,champion_df,how = 'left', left_on = 'championId', right_on = 'key')
# 중복되는 값 삭제
summary = summary.rename(columns={'name': 'Champion'}).drop(['championId','key'],axis = 1)
summary_View = summary[['subType','Champion','BLUE_WR','RED_WR','unbalanced','p_value']]
summary_View[summary_View['unbalanced']].sort_values('p_value')
subType Champion BLUE_WR RED_WR unbalanced p_value
180 450 모르가나 0.682292 0.462312 True 0.000006
169 450 사이온 0.744186 0.380952 True 0.000289
195 450 잔나 0.629630 0.412281 True 0.000462
131 420 키아나 0.244898 0.526316 True 0.001823
244 450 피즈 0.544554 0.343750 True 0.003771
  • 최종 자료를 보면 모르가나 사이온 등은 진영에 따라 승률 차이가 커보인다.

  • 일반적으로 사람들이 블루팀이 유리하다고 생각하지만 여기서 유불리 유무는 진영에 따라 승률 차이가 있는지 검증이다.

  • True라고 블루팀이 유리하다는 것이 아니며 앞서 유의확률 생성시 양쪽 검증 기준으로 만들었음을 기억하자.

Leave a comment