[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