본문 바로가기

리그오브레전드/리그오브레전드 데이터 분석

[리그오브레전드 데이터 분석] 리그오브레전드 API 탐색 - 3

320x100
game_id=r.json()[0] #'KR_5475389572'

request_header={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36",
    "Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
    "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
    "Origin": "https://developer.riotgames.com",
    "X-Riot-Token": "RGAPI-2c6804dd-3f78-4975-b3a1-62bbbc4a1f77"
}
summoner_api = 'https://asia.api.riotgames.com/lol/match/v5/matches/' + game_id + '/timeline'
r = requests.get(summoner_api,headers=request_header)
df=pd.DataFrame(r.json())
df

이전에 사용했던 최근 게임을 갖고 이어서 계속 게임 정보를 찾아보자.

이전까지 얻은 정보는 게임이 끝난 시점의 기준의 정보였다면

이제 얻을 정보는 게임 중간 과정들에 대한 정보라고 할 수 있다.

이전에 사용했던 API 주소에 /timeline만 추가하면 해당 API를 사용할 수 있다.

 

df는 이런 식으로 생겼는데,

participants는 말 그대로 참가자 정보이다.

 

pd.DataFrame(df['info']['participants'])

당장 필요한 정보는 아니지만, 나중에 다른 분석에 활용할 수도 있으니

다른 플레이어의 puuid 정보도 들어가있다는 사실은 기억해두자.

 

그렇다면 이제 분석에 쓸만한 내용으로 보이는 건

info 행의 frames 열만 남았다.

 

pd.DataFrame(pd.DataFrame(df['info']['frames']))

frames 에는 events, participantFrames 두 개 행이 들어있다.

여기서 왜 38열인지 짚고 넘어가야할 필요가 있다.

 

 

지금 사용하고 있는 게임을 fow로 보았을 때, 플레이 시간이 36분 15초이다.

그렇다. 이건 해당 게임의 총 플레이 시간과 연관이 있다.

그럼 첫번째 열을 뜯어보자.

 

pd.DataFrame(pd.DataFrame(pd.DataFrame(df['info']['frames']))['participantFrames'][0])

participantFrames의 첫번째 열을 보면,

10개의 참가자의 챔피언 스탯, 현재 골드, 데미지 정보(준 피해량 받은 피해량 등), 경험치 정보가 들어있다.

그리고 해당 데이터는 전부 경험치 0, 골드 500, 데미지 정보도 전부 0으로 되어있다. 

롤을 해봤다면 이것이 무슨 의미인지 알 것이다.

게임이 시작됐을 때의 시작 골드가 500이고, 경험치, 피해량 등은 전부 당연히 0이다.

따라서 이 첫번째 열의 ChampionStats의 챔피언 능력치는, 아이템이나 레벨업 등의 변수가 적용되기 전 온전한 챔피언고유의 1렙 능력치일 것이다. 

pd.DataFrame(pd.DataFrame(pd.DataFrame(df['info']['frames']))['events'][0])

events의 첫번째 열을 보면,

type에 PAUSE_END만 적혀있다.

 

위 정보들을 종합해보면, 첫번째 열은 게임이 로딩되고 게임이 딱 시작했을 때,

타임스탬프가 0인 시점의 정보다.

 

그렇다면 왜 38열이었는지 해석이 가능하다.

36분 15초 게임이기 때문에

 

0 : 게임 시작 00초

1 : 게임 시작 후 00초 ~ 59초

2 : 게임 시작 후 60초 ~ 119초

.

.

37 : 게임 시작 후 36분 ~ 36분 15초

 

총 38열이 된 것이다.

 

pd.DataFrame(pd.DataFrame(df['info']['frames'])['events'][1])

그럼 이건

게임 시작 후 00초 ~ 59초

즉, 게임 시작 후 1분 사이 일어난 이벤트들이다.

참고로 timestamp는 1000이 실제 시간 기준 1초다.

60초는 timestamp 기준 60000이 되며, 60000 이상은 1분 이후 데이터이므로 이 1행에는 존재하지 않는다.

00초에는 PAUSE_END만 있었던반면, 1분 사이 많은 event들이 일어났다.

 

데이터를 보면, 참가자 8번이 아이템을 가장 빨리 구매했다.

timestamp가 동일한데 아이템은 두 번 산 것이 되는데, 0.001초 사이에 아이템 두 개를 살만큼 손이 빠를리는 없고

최근에 추가된 한번에 시작아이템을 사는 기능을 통해 산 것임을 알 수 있다. ( ex 도란검+포션하나 )

 

또한 각 플레이어들이 어떤 스킬을 찍는지까지 확인할 수 있고,

29~30행을 보면, 플레이어 2번은 1분도 되지 않았는데 벌써 아이템을 팔고, 구매한 것을 알 수 있다.

아이템을 되돌린 것도 아니고 팔고 산다니.

잘못 해석한건가? 아니면

서포터가 주문 도둑검을 들고 돈 번 뒤에 포션을 하나 팔고 핑크 와드를 샀었나싶어 직접 해당 경기를 확인해봤다.

 

 

확인해보니, 상대 렉사이가 와드를 박고 귀환해서 와드 장신구를 팔고 렌즈로 바꾼 것이었다.

시간은 1분 직전. 모든 정보가 일치한다.

pd.DataFrame(df['info']['frames'])['events'].apply(lambda x : pd.DataFrame(x)['type']).T.iloc[:20]

37분까지 일어난 event들의 type을 살펴보면

갈수록 다양한 event가 발생하는 걸 알 수 있다.

후반으로 갈수록 챔피언 킬, 와드 설치 및 제거, 타워 철거가 일어나기 때문이고

PAUSE_END와 GAME_END를 통해 게임의 시작과 끝을 알린다.

 

type_list=[]
pd.DataFrame(pd.DataFrame(df['info']['frames'])['events'].apply(lambda x : type_list.append(pd.DataFrame(x)['type'].unique())))
list(set(np.concatenate(type_list).tolist()))

한 게임동안 총 발생했던 각 event의 type의 고유 살펴보면, 다음과 같았다.

앞으로 분석할 수 있는 요소이니 기억해두자

event_cnt=pd.DataFrame(pd.DataFrame(pd.DataFrame(df['info']['frames'])['events'][0]).groupby('type').count()['timestamp']) #0분 event
for i in range(1,len(pd.DataFrame(df['info']['frames'])['events'])):           
    event_cnt=orin.add(pd.DataFrame(pd.DataFrame(pd.DataFrame(df['info']['frames'])['events'][i]).groupby('type').count()['timestamp']),fill_value=0)
event_cnt

이 36분의 게임에서 발생한 event들의 수를 합산해봤다.

timestamp를 count한 값을 더했는데, 이유는 각 event별로 적용되는 컬럼의 값이 다르기 때문에

공통적으로 포함된 컬럼은 발생 시간인 timestamp뿐이었기 때문이다.

(위의 1분 이벤트 DataFrame을 참고하면 될 듯)

 

event_df=pd.DataFrame(event_cnt.reset_index())
event_df.columns=['이벤트','횟수']
event_df['비율']=round(event_df['횟수']/event_df['횟수'].sum()*100,1)
event_df=event_df.sort_values(by='비율' ,ascending=False).reset_index()[['이벤트','횟수','비율']]
event_df

import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family'] = 'Malgun Gothic'

ratio = event_df['비율']
labels = event_df['이벤트']

plt.pie(ratio, labels=labels, autopct='%.1f%%')
plt.legend(labels=labels,bbox_to_anchor=(1.7,1),loc=2,borderaxespad=0.)
plt.show()

아이템 구매, 판매가 총 40%, 와드 설치, 제거가 총 20%이었다.

레벨업, 스킬 레벨업도 높은 비율이었고, 킬 관련 이벤트는 5%도 되지 않았다.

 

item_list=[]
for i in range(len(pd.DataFrame(df['info']['frames'])['events'])):
    item_list.append(int(pd.DataFrame(pd.DataFrame(pd.DataFrame(df['info']['frames'])['events'][i])['type'] == 'ITEM_PURCHASED').sum()))
item_df = pd.DataFrame(item_list).reset_index()
item_df.columns = ['시간','아이템 구매 횟수']
sns.lineplot(data=item_df,x="시간",y="아이템 구매 횟수",ci=None).set_title("시간당 킬 수")
plt.show()
kill_list=[]
for i in range(len(pd.DataFrame(df['info']['frames'])['events'])):
    kill_list.append(int(pd.DataFrame(pd.DataFrame(pd.DataFrame(df['info']['frames'])['events'][i])['type'] == 'CHAMPION_KILL').sum()))
kill_df = pd.DataFrame(kill_list).reset_index()
kill_df.columns = ['시간','킬 수']
sns.lineplot(data=kill_df,x="시간",y="킬 수",ci=None).set_title("시간당 킬 수")
plt.show()

시간당 아이템 구매 횟수를 보면, 극초반 단계를 제외하고는 대체적으로 5개 이상의 아이템 구매가 이루어진다.

(단, 한 명이 여러 아이템을 사는 것도 전부 계산된다는 것은 주의해야함) (ex 롱소드(1)+포션(1)+핑크와드(1) )

시간당 킬 수는 라인전이 끝나고 대치 상태가 되는 중반 단계에서 오히려 킬 수가 잠시 적어지는 것을 알 수 있다.

또한 아이템이 폭발적으로 많이 구매되는 시기는 킬이 많이 발생한 시기와 비슷한 것을 알 수 있다.

안전하게 라인 밀고 복귀해서 아이템을 사는 경우도 많겠지만, 한타에서 죽은 뒤 흑백 화면을 보며 아이템을 구매하는 경우도 많기 때문이다.

 

아직 바로 분석을 하기엔 경기가 턱없이 부족하지만, 게임 데이터를 모은다면 꽤 이런저런 분석들이 가능하겠다는 생각이 든다.

 

이제 남은 문제는 역시나 내가 분석할 퀸이 포함될 경기를 수집하는거다.

우선 다음으로 내가 해야할 것은

1. 최대한 많은 게임 데이터 수집 

2. 퀸이 있는 경기만 필터링

 

아직 내가 모르는 것은 API로 얼마나 많은 게임 경기를 수집할 수 있을지다.

한 개의 API 키로 몇 개까지 수집이 가능할지 모른다.

다만 하나 꼼수가 떠오른 것은 FOW에 있는 퀸 장인랭킹 리스트를 참고하는 것이다.

퀸 장인들은 퀸을 많이 플레이할 것이고, 퀸이 있는 경기만 필터링했을 때 게임에 퀸이 있을 확률이 많이 높아질 것이다.

다음은 퀸 장인 리스트를 크롤링하고, API에서 각 ID당 quuid를 받아와서, 최근 랭크 게임을 수집하여 DB를 만들어보려고 한다.

반응형