Data Analysis & Study

리그오브레전드 데이터 분석 - match data EDA(2) 본문

python/Riot API를 활용환 데이터 분석

리그오브레전드 데이터 분석 - match data EDA(2)

사용자 me_nyong2 2020. 3. 15. 17:13

안녕하세요. 이번 포스팅은 저번에 이어서 연속형 데이터에 관한 분석을 진행해보도록 하겠습니다.

 

저번 포스팅을 간략하게 요약하자면 범주형 변수(1/0)에 대한 시각화를 진행했엇는데요. 결과적으로 우리가 알고 있던 자명한 사실들을 데이터적으로 확인할 수 있엇습니다.

 

본격적으로 연속형 데이터 분석을 진행하도록 하겠습니다.

 

아 그리고 해당 포스팅을 읽기전에 처음 읽으시는 분들은 1번째 포스팅을 먼저 읽어주시면 감사하겠습니다.(데이터를 받으실 수 있거든요 ㅎㅎ)

 

 

1. 데이터 불러오고 처리하기

이 부분은 이전 포스팅에서 자세히 다루고 있으므로 코드 한개에 담겠습니다.

import pandas as pd
import pickle
import matplotlib.pyplot as plt
import numpy as np
import sys
import os
from pandas.io.json import json_normalize
import seaborn as sns

#데이터 불러오기
lol_df = pd.read_pickle('/Users/sinmin-yong/GitHub/모든저장소데이터셋/파일 저장용(my-study)/롤17500개.pickle')
lol_df.head()

#a팀 b팀 분리
team_a_error = []
team_b_error = []
team_a = pd.DataFrame()
team_b = pd.DataFrame()
for i in range(len(lol_df)):
    if i % 1000 == 0:
        print(str(i)+'행 처리중')
    try:
        team_a = team_a.append(json_normalize(lol_df['teams'].iloc[i][0]))
        team_b = team_b.append(json_normalize(lol_df['teams'].iloc[i][1]))
    except:
        team_a_error.append(i)
        team_b_error.append(i)
        print(str(i)+'행에서 오류')
        pass
        
# 팀a와 팀 b의 승패가 반대인지 데이터 정합성 검정 -모두 반대임을 확인
for i in range(len(team_a)):
    wf_valid = team_a['win'].iloc[i]
    
    if team_b['win'].iloc[i] != wf_valid:
        pass
    else:
        print(str(i)+'행 데이터 정합성 문제')
        
#각 경기별 게임 시간 병합
lol_df = lol_df.drop(index = team_a_error)

team_a['gameDuration'] = lol_df['gameDuration'].tolist()
team_b['gameDuration'] = lol_df['gameDuration'].tolist()

lol_df['gameDuration'].index = range(len(lol_df))
team_a['gameDuration'].index = range(len(team_a))
team_b['gameDuration'].index = range(len(team_b))

game_df = pd.concat([team_a,team_b],axis=0)

#분석의 용이성을 위해서 타겟 데이터를 제외한 범주형 데이터를 인코딩
'''
True : 1
False : 0
'''
tf_mapping = {True:1,False:0}
bool_column = game_df.select_dtypes('bool').columns.tolist()

for i in bool_column:
    game_df[i] = game_df[i].map(tf_mapping)
    
wl_mapping = {'Win':'Win','Fail':'Lose'}
game_df['win'] = game_df['win'].map(wl_mapping)

#게임 시간별 데이터 segment
#n_tile로 게임시간을 분위수로 파악
game_part1 = game_df[game_df['game_time']>=30].sort_values('win')
game_part2 = game_df[game_df['game_time']>=40].sort_values('win')
game_part3 = game_df[game_df['game_time']<30].sort_values('win')
game_part4 = game_df[game_df['game_time']<20].sort_values('win')

#시각화 함수
def first_valid_visualize(df,target,variable):
    sns.factorplot(target,variable,data=df)
    plt.title(variable+' 변수의 승리확률')
    #plt.xticks(df[target])
    plt.show()

여기까지 완료했다면, 분석을 하기 위한 데이터 셋 구축은 완료하신겁니다.

 

 

2. 연속형 데이터 EDA

first_valid_visualize(game_df,'win','towerKills')
plt.show()

이전과 같이 연속형 데이터 시각화를 진행하겠습니다.

 

이긴 팀의 타워를 부신 개수가 평균적으로 많은 것을 확인할 수 있습니다.

 

이긴 팀의 바론을 처치한 횟수가 평균적으로 높은 것을 확인할 수 있습니다.

 

이긴 팀의 드래곤을 처치한 횟수가 평균적으로 높은 것을 확인할 수 있습니다.

 

해당 데이터 셋에서 경기별로 팀별로 몇킬이 나왔는지, 바위게는 어느팀에서 더 많이 먹었는지, 설치한 와드개수는 몇개인지 등의 데이터까지 있엇으면 더 재미있는 분석이 되었을텐데 해당 데이터는 없는게 아쉬웠습니다. 일단 해당 경기 id값과 연결되는 다른 데이터가 있는지 api를 더 확인해봐야겠습니다.

 

2-1. 경기 시간 segment 데이터별 EDA

그 다음은 경기 시간별 나눠놓은 4개의 데이터 셋을 바탕으로 분석을 진행해보겠습니다.

 

시각화를 하기 전에 먼저 각 경기 세그먼트별 연속형 데이터의 수치별 승패 비율을 확인하겠습니다.

ex) 20분 이하 게임에서 드래곤을 0개~n개 를 먹었을 때 승패 비율

#각 경기 시간 세그먼트 데이터별 sequence column의 수치별 승패 비율 차이
def sequence_var_inf(variable):
    global game_part1, game_part2, game_part3, game_part4
    
    value1 = game_part4[variable].value_counts().keys().tolist()
    value2 = game_part3[variable].value_counts().keys().tolist()
    value3 = game_part1[variable].value_counts().keys().tolist()
    value4 = game_part2[variable].value_counts().keys().tolist()
    
    value1.sort()
    value2.sort()
    value3.sort()
    value4.sort()
    print('=============================')
    print('GameTime < 20 minute')
    print('=============================\n')
    for i in value1:
        try:
            a = game_part4[game_part4[variable]==i]['win'].value_counts().values[0]
        except:
            a = 0
        try:
            b = game_part4[game_part4[variable]==i]['win'].value_counts().values[1]
        except:
            b = 0
        print(variable+' 변수가 '+str(i)+' 일 때\n Win : {}\n Lose : {}\n'.format(a,b))
    
    print('=============================')
    print('GameTime < 30 minute')
    print('=============================\n')
    for i in value2:
        try:
            a = game_part3[game_part3[variable]==i]['win'].value_counts().values[0]
        except:
            a = 0
        try:
            b = game_part3[game_part3[variable]==i]['win'].value_counts().values[1]
        except:
            b = 0
        print(variable+' 변수가 '+str(i)+' 일 때\n Win : {}\n Lose : {}\n'.format(a,b))
        
    print('=============================')
    print('GameTime > 30 minute')
    print('=============================\n')
    for i in value3:
        try:
            a = game_part1[game_part1[variable]==i]['win'].value_counts().values[0]
        except:
            a = 0
        try:
            b = game_part1[game_part1[variable]==i]['win'].value_counts().values[1]
        except:
            b = 0
        print(variable+' 변수가 '+str(i)+' 일 때\n Win : {}\n Lose : {}\n'.format(a,b))
        
    print('=============================')
    print('GameTime > 40 minute')
    print('=============================\n')
    for i in value4:
        try:
            a = game_part2[game_part2[variable]==i]['win'].value_counts().values[0]
        except:
            a = 0
        try:
            b = game_part2[game_part2[variable]==i]['win'].value_counts().values[1]
        except:
            b = 0
        print(variable+' 변수가 '+str(i)+' 일 때\n Win : {}\n Lose : {}\n'.format(a,b))

 

함수를 작성한 뒤 결과값들을 확인해보겠습니다.


sequence_var_inf('dragonKills')

게임 경기가 길어질수록 드래곤 처치에 개수가 승리에 미치는 영향이 평균적으로 감소하는 것을 알 수 있습니다.

 

바론을 처치한 횟수 또한 경기 초반부들의 기록을 살펴보면 바론을 처치한 것이 게임 승리를 굳히는 역할을 하지만 경기가 길어질수록 한타 한번에 역전되는 경우가 많듯이 긴 경기일수록 처치한 바론 횟수가 승리에 미치는 영향이 감소하는 것을 확인할 수 있습니다.

심지어 30분이상, 40분이상의 경기기록으로 미루어보았을 때 바론처치 1번은 초반 경기들과 다르게 승리 패배 비율이 거의 차이 없는 것을 확인할 수 있습니다.

 

억제기 같은경우 1개가 깨졌다고해서 장기 게임에서는 큰 영향을 안주는 것을 확인가능하지만, 30분 이하의 게임들에서는 억제기 1개가 깨진 것에는 승패에 큰 영향을 보이는 것을 확인가능합니다.

 

타워를 부신 경우도 위의 결과들과 같은 양상을 보여 따로 보여드리진 않겠습니다. 한번 저 코드대로 실행해보시고 직접 결과를 확인해보시면 될 것 같습니다!

 

마지막으로 각 segment별로 해당 수치형 데이터들이 이긴 팀과 진팀에 어떤 비율로 반영되어 있는지 확인해보겠습니다.


def first_time_ratio(target,variable):
    global game_part1, game_part2, game_part3, game_part4
    

    fig = plt.figure(figsize=(20,20))
    fig.suptitle('게임 시간대별 승리 팀과 패배 팀의 ' + variable + ' 비율',size = 30)
    ax1 = fig.add_subplot(221)
    ax2 = fig.add_subplot(222)
    ax3 = fig.add_subplot(223)
    ax4 = fig.add_subplot(224)
    
    ax1.set_title('GameTime < 20 minute',size = 20)
    ax2.set_title('GameTime < 30 minute',size = 20)
    ax3.set_title('GameTime > 30 minute',size = 20)
    ax4.set_title('GameTime > 40 minute',size = 20)
    
    
    sns.factorplot(target,variable,data=game_part4,ax=ax1)
    #plt.title(variable+' 변수의 승리확률(GameTime < 20 minute)')
    #plt.show()
    
    
    sns.factorplot(target,variable,data=game_part3,ax=ax2)
    #plt.title(variable+' 변수의 승리확률(GameTime < 30 minute)')
    #plt.show()

    sns.factorplot(target,variable,data=game_part1,ax=ax3)
    #plt.title(variable+' 변수의 승리확률(GameTime >= 30 minute)')
    #plt.xticks(df[target])
    #plt.show()
    
    sns.factorplot(target,variable,data=game_part2,ax=ax4)
    #plt.title(variable+' 변수의 승리확률(GameTime >= 40 minute)')
    #plt.xticks(df[target])
    #plt.show()
    #plt.close(g.fig)
    #plt.show()

이전 포스팅에서 시각화한 코드와 같은 코드입니다!

범주형 변수들의 결과와는 다른 결과가 도출되었는데요. 이는 마지막 부분에서 확인해보겠습니다.

 


first_time_ratio('win','dragonKills')
plt.show()

 

범주형 변수들과의 결과와 다르게 드래곤을 처치한 횟수는 장기 게임으로 갈수록 높은 것을 확인할 수 있습니다.

 

타워를 부신 개수도 승리한 팀이 평균적으로 높습니다.

 

바론을 처치한 횟수도 영향력만 약해지지 승리한 팀에서 평균적으로 더 높은 것을 확인가능합니다.

 

억제기를 부신 개수 또한 승리한 팀에서 평균적으로 높은 것을 확인 가능합니다.

 

범주형 변수와 연속형 변수들과의 결과 차이가 왜 도출되었는지는 아마 몇몇분들은 알아채셨을 수도 있는데요. 범주형 변수는 처음으로 처치하거나 부신 여부를 나타내는 데이터인데 이는 어느정도 승리팀에서 높은 확률로 나타나는 것을 확인할 수 있지만 단순히 처음 처치했을 뿐이지 몇개를 뿌셧거나, 처치했는지는 알 수 없습니다.

 

그래서 first_column 데이터는 후반부 게임으로 갈수록 승리에 대한 영향력이 떨어지며 오히려 극 후반 게임일수록 지표가 역전되는 case도 이전 분석에서 확인할 수 있엇습니다.

 

그러나 이번 분석은 여부가 아니라 수치(몇개)를 분석하는 것이었으므로, 당연히 후반부 게임일지라도 이긴 팀에서 바론, 억제기, 타워 등의 변수의 평균이 높은 사실을 알 수 있던 것입니다.

 

하지만 후반부 게임일수록 한타 한 번에 역전되는 경우가 많기 때문에 영향력은 점차 감소되는 것을 승/패 비율, 평균으로 지금까지 확인했습니다. 참으로.. 데이터는 파고들고 파고들수록 새로운 사실들, 기존에 아는 사실이더라도 "와 이게 진짜네" 하는 결과들을 많이 보여주는 것 같습니다.

 

이번 분석에서 아쉬웠던 점은 중간에도 언급했지만, 더 경기에 대한 다양한 데이터가 없다는 점이었습니다. 이부분은 제가 모든 api를 고려해서 수집한 것이 아니기 때문에 좀 더 많은 부분(컬럼, 데이터)에 대해서 체크한 뒤 여러분들이 더 다양한 데이터로 분석하실 수 있도록 데이터를 구축해보겠습니다.

 

3. 다음 글 예고

다음은 해당 분석의 마지막 파트인 지금까지 분석했는 변수들이 승/패에 얼마만큼 관련이 있고, 영향을 끼쳤는지, 인과관계가 존재하는지를 상관분석과 Logistic Regression을 통해서 알아보겠습니다.

 

데이터가 필요하신 분들은 댓글에 이메일을 남겨주세요

 


2 Comments
  • 프로필사진 전계현 2020.03.19 17:50 안녕하세요 라이엇 API를 이용해 통계 웹을 만들고 싶은 초보 개발자 입니다 혹시 우리팀 4명의 챔피언과 상대팀 5명의 챔피언을 넣고
    내가 무슨픽을 했을때 가장 승률이 높았는가를 알수있을까요? 그걸 알수있는 API가 있는지 또는 신뢰가 가능할만큼의 표본데이터양을 받아올수 있는건지 궁금합니다
    ansrl321@naver.com
  • 프로필사진 사용자 me_nyong2 2020.03.19 22:09 신고 좋은 인사이트인 것 같습니다! 흠... 제가 생각했을 때 그러기 위해서는 각 조합별로 승률을 산출할 수 있는 데이터가 있어야 할텐데 지금 데이터 셋으로는 가능할 것 같지는 않구요.
    드릴 수 있는 답변은 일단 api를 통해서 데이터를 꾸준히 쌓아주시고, 지금 상태로는 어떤 챔피언끼리(한 두 챔피언정도?) 조합일 때 승리확률이 높아지는지 패배확률이 높아지는지는 할 수 있을 것 같습니다. 아마 추천시스템을 이용하면 승리에 영향을 줄 수 있는 최적의 조합을 추천해주는 사이트는 만드실 수 있을 것 같아요.
    예를 들어 알리스타를 입력했을 때 야스오를 반환해준다던가 그런식으로요!

    신뢰가능할만큼의 표본성을 갖추려면 일단 티어별로도 따로 산출해야 되기 때문에 정확히 산출하기는 힘들지만 제한된 라이엇 api를 꾸준히 이용해서 수집한다면 신뢰성이 바탕된 데이터 셋을 구축할 수 있을 것이라고 생각합니다.

    데이터는 곧 보내드릴게요~
댓글쓰기 폼