Python을 기반으로 개발을 진행하였습니다.
1. PUBG API 불러와 집계하기
플레이어 ID를 입력하게 되면 해당 palyer의 PLAYERS API json 파일이 반환됩니다.
json 파일에서 필요한 정보인 ID를 추출하여 DataFrame의 형태로 만들어 주는 함수를 구현합니다.
import requests
import pandas as pd
from collections import Counter
# player의 게임당 id를 api에서 받아와 Dataframe으로 만드는 함수
def get_stats_dataframe(url, header):
r = requests.get(url, headers=header)
match_data_json = r.json()
match_data_df = pd.DataFrame(match_data_json['data'][0]['relationships']['matches']['data'])
return match_data_df
type | id | |
0 | match | 38ab8706-cd24-4eaf-ac66-cc6ef5bf98b5 |
. . . |
. . . |
. . . |
43 | match | b438f062-26cd-44af-b48d-1fa7faf42279 |
44 | match | 4e6c513f-a97e-4e35-a9f6-f860114c2b87 |
get_stats_dataframe 함수를 실행시킨 결과를 출력하게 되면 위와 같이 출력 되고, 검색한 user의 불러온 게임 수는 44판이 됩니다.
다음은 가져온 모든 게임에서 게임 내 데이터를 MATCHES API를 통해 불러오는 함수를 작성합니다.
# 게임 모드 리스트와 player의 게임 플레이 데이터를 각각 list와 Datafreame으로 만드는 함수
def get_user_stats(user_name, url, header):
# API를 통해 플레이어의 게임당 통계 데이터 가져오기
player_data_df = get_stats_dataframe(url, header)
ilist, id_list, game_modes = [], [], []
# 각 경기의 통계 데이터를 가져와 리스트에 추가
for i in range(len(player_data_df)):
match_url = f'https://api.pubg.com/shards/kakao/matches/{player_data_df["id"][i]}'
match_r = requests.get(match_url, headers=header)
match_data = match_r.json()
# 경기 모드 추가
game_modes.append(match_data['data']['attributes']['gameMode'])
for entry in match_data['included']:
# 'participant' 타입이면 'id' 정보를 아이템에 담아 'ilist' 리스트에 추가합니다.
if entry['type'] == 'participant':
ilist.append({'id': entry['id'], **entry['attributes']['stats']})
# 'roster' 타입이면 포함된 'data' 객체에 'rankx' 정보를 추가하며 'id_list'에 추가합니다.
elif entry['type'] == 'roster':
rank = entry['attributes']['stats']['rank']
data_list = entry['relationships']['participants']['data']
id_list.extend({'rankx': rank, **data} for data in data_list)
# 플레이어와 관련된 통계 데이터를 데이터프레임으로 변환
participant_id_df = pd.DataFrame(id_list)
matches_df = pd.DataFrame(ilist)
merged_df = pd.merge(left=matches_df, right=participant_id_df, how='inner', on='id')
# 사용자 이름과 일치하는 데이터 필터링
mask = merged_df['name'] == user_name
one_user_data = merged_df[mask]
return one_user_data, game_modes
DBNO | sassists | boosts | damageDealtdeath | Type | headshotKills | heals | killPlace | killStreaks | kills | ... | swimDistance | teamKills | timeSurvived | vehicleDestroys | walkDistance | weaponsAcquired | winPlace | id | type | rankx |
0 | 0 | 1 | 194.969510 | byplayer | 0 | 1 | 51 | 0 | 0 | ... | 0.0 | 0 | 944 | 0 | 1563.200300 | 6 | 13 | e642f2f3-7f3c-4f50-aba7-6b1e1bad8601 | participant | 13 |
1 | 1 | 1 | 222.745000 | byplayer | 0 | 1 | 17 | 1 | 1 | ... | 0.0 | 0 | 368 | 0 | 402.345460 | 3 | 8 | d1144551-ca53-4cd5-b1dc-7e944210d5f1 | participant | 8 |
. . . |
||||||||||||||||||||
0 | 0 | 0 | 0.000000 | byplayer | 0 | 0 | 43 | 0 | 0 | ... | 0.0 | 0 | 630 | 0 | 537.105400 | 4 | 10 | 53f89f92-d54d-4d17-aac8-54e7a38c4cea | participant | 10 |
['squad', 'squad', ...., 'squad', 'squad']
get_user_stats 함수를 출력하게 되면 위와 같은 DataFreame과 게임 모드가 담긴 list가 출력됩니다.
위의 코드에 대해 부연 설명을 하자면 id를 통해 불러온 json 파일 안에 'type'이 "participant"이면 게임의 정보가 담겨 있고 "roster"이면 팀 id, 승리 여부, rank의 정보가 담겨있습니다.
participant에는 rank 정보가 없기 때문에 rank 정보를 DataFrame에 담기 위해 roster의 rank 정보를 따로 리스트에 담아 DataFrame으로 만든 뒤 participant에 담겨있는 정보와 id를 기준으로 join 합니다.
최종적으로 만들어진 DataFramed의 열 정보는 다음과 같습니다.
# columns 설명
- DBNOs : 쓰러트린 플레이어 수
- assists : 어시스트
- boosts : 사용된 부스트 아이템 수
- damageDealt : 총 피해량, * 자해피해량은 차감 됩니다.
- deathType : 죽음 유형
- headshotKills : 헤드샷 킬 수
- heals : 사용한 치유 아이템 수
- killPlace : 킬을 기반으로 한 해당 플레이어의 순위
- killStreaks : 연속 킬 수
- kills : 킬 수
- longestKill : 최장 킬
- name : 플레이어 닉네임
- playerId : 계정 ID
- revives : 팀원을 소생시킨 횟수
- rideDistance : m단위의 승차거리
- roadKills : 로드킬 수
- swimDistance : m단위의 수영거리
- teamKills : 팀킬 수
- timeSurvived : 초단위로 측정된 생존 시간
- vehicleDestroys : 차량파괴한 수
- walkDistance : m단위의 도보로 이동한 거리
- weaponsAcquired : 획득한 무기
- winPlace : 경기에서 이선수의 위치(1~130)
- rankx : 경기에서 팀의 순위
|
다음은 user의 게임 전적 및 간단한 통계를 출력하는 함수입니다.
def print_user_stats(one_user_data, game_modes, user_name):
# 플레이어 통계 출력
avg_kills = round(one_user_data['kills'].mean())
win_rate = round(len(one_user_data[one_user_data['rankx'] == 1]) / len(one_user_data) * 100)
max_kill_distance = round(one_user_data['longestKill'].max())
avg_rank = round(one_user_data['rankx'].mean())
death_count = len(one_user_data['deathType'] != 'alive')
kill_sum = one_user_data['kills'].sum()
assists_sum = one_user_data['assists'].sum()
total_kda = round((kill_sum + assists_sum) / death_count)
most_kill = one_user_data['kills'].max()
lowest_kill = one_user_data['kills'].min()
avg_time_survived = round(one_user_data['timeSurvived'].mean() / 60) # 초단위로 저장되 있기 때문에 분단위로 변경
avg_damage_dealt = round(one_user_data['damageDealt'].mean())
game_count = len(one_user_data)
print(f"------------ {user_name}님의 통계 ------------")
print(f"게임 수: {game_count}게임")
print(f"평균 킬 수: {avg_kills}킬")
print(f"승률: {win_rate}%")
print(f"최대 거리 킬: {max_kill_distance}m")
print(f"평균 등수: {avg_rank}등")
print(f"전체 KDA: {total_kda}")
print(f"최다 킬: {most_kill}")
print(f"최저 킬: {lowest_kill}킬")
print(f"평균 생존 시간: {avg_time_survived}분")
print(f"평균 딜량: {avg_damage_dealt}")
print(f"가장 많이 플레이한 게임 모드: {Counter(game_modes).most_common(n=1)[0][0]}")
마지막으로 API-KEY를 통해 데이터를 불러오는 코드입니다.
if __name__ == "__main__":
user_name = input("user name를 입력하세요: ")
url1 = 'https://api.pubg.com/shards/kakao/players?filter[playerNames]='
url = url1 + user_name
header = {
"Authorization": "API-KEY",
"Accept": "application/vnd.api+json"
}
one_user_data, game_modes = get_user_stats(user_name, url, header)
get_user_stats(user_name, url, header)
print_user_stats(one_user_data, game_modes, user_name)
if __name__ == "__main__": 구문이 있는 경우 해당 조건문 안의 코드는 메인 프로그램으로 실행될 때만 수행되고, 다른 파일에 모듈로써 임포트될 때에는 실행되지 않습니다.
이렇게 구현하면, 해당 파일을 임포트하는 다른 스크립트에서 함수나 변수 등 해당 파일의 기능을 사용할 수 있지만, 메인 코드를 실행하는 데 필요한 전체 프로세스를 직접 실행시키지는 않습니다. 이렇게 함으로써 모듈화가 쉽고 코드 재사용성이 높아집니다.
2. 코드 실행
위의 전체 코드를 실행시키고 user name에 breakthebalance를 입력하게 되면 아래와 같은 결과물을 출력합니다.
------------ breakthebalance님의 통계 ------------
게임 수: 46게임
평균 킬 수: 3킬
승률: 52%
최대 거리 킬: 476m
평균 등수: 3등
전체 KDA: 6
최다 킬: 10킬
최저 킬: 0킬
평균 생존 시간: 25분
평균 딜량: 574
가장 많이 플레이한 게임 모드: squad
'게임 데이터 분석' 카테고리의 다른 글
[게임 데이터 분석 #5] MongoDB를 사용한 데이터 저장 (1) | 2023.06.30 |
---|---|
[게임 데이터 분석 #4-1] TELEMETRY API를 활용해 판별 MAP 이미지 위에 이동 경로 그리기 (1) | 2023.06.25 |
[게임 데이터 분석 #4] TELEMETRY API를 활용해 MAP 이미지 위에 이동 경로 그리기 (0) | 2023.06.19 |
[게임 데이터 분석 #3] chicken-dinner를 활용한 리플레이 애니메이션 출력하기 (0) | 2023.06.14 |
[게임 데이터 분석 #2] PUBG API를 활용한 판별, 팀별 통계 (0) | 2023.06.11 |