- 개요
이번 포스트에서는 챌린저스 1, 2, 3, 4 월드의 직업 분포를 확인하겠습니다.
아무 말도 없으면 챌린저스 1 월드만 분석하는 것이고, 챌린저스 1 월드 이외 다른 월드도 분석할 때는 지금처럼 설명을 하겠습니다.
- 직업 칼럼의 이해
직업에 대한 정보가 있는 칼럼의 이름은 class_name과 sub_class_name입니다.
가장 첫 줄처럼 시그너스 기사단의 직업 이름은 class_name이 기사단이고, sub_class_name이 우리가 흔히 부르는 직업명인 스트라이커입니다.
은월은 영웅이라는 class_name에 속하고 sub_class_name이 은월일 것 같지만
두 번째 줄을 보면 class_name이 은월이고 sub_class_name이 NaN값입니다.
우리가 흔히 아는 직업명은 class_name일수도, sub_class_name일 수도 있죠.
이처럼 class_name과 sub_class_name 중 하나의 칼럼만으로 캐릭터의 직업 데이터 분석 할 수 없습니다.
- 직업 칼럼 생성
class_name과 sub_class_name 칼럼을 활용해서 직업명을 저장하는 merged_class_name이란 칼럼을 만들겠습니다.
이 작업은 df.apply(lambda row: ~~) 문법을 쓰면 쉽게 할 수 있습니다.
lambda는 한 줄짜리 함수를 만들어주는 역할을 하고, row는 함수의 입력값입니다.
apply는 괄호 안의 함수를 pandas.DataFrame()에 적용하는 역할을 하며, row는 행 순서대로 넣는 것입니다.
실제로 동작하는 과정을 간단히 설명하자면 가장 처음에는 df의 1행(row)이 lambda 문법 함수의 입력값으로 들어갑니다.
if pd.notna(row['sub_class_name']) else row['class_name']에서는 1행의 sub_class_name 칼럼의 값이 NaN이 아니면 sub_class_name 칼럼을 값을, 그렇지 않다면 class_name 칼럼의 값을 선택합니다.
이렇게 1행부터 마지막 행까지 위의 작업을 반복합니다.
마지막으로 axis = 1은 위에서 구한 값을 칼럼으로 저장하기 위한 옵션이며, 위의 값이 merged_class_name 칼럼에 저장됩니다.
import pandas as pd
# 파일 읽는 부분
fnlist = ['../data/chall_rank_more/2025-01-16_1_100000.csv',
'../data/chall_other/2025-01-16_2.csv',
'../data/chall_other/2025-01-16_3.csv',
'../data/chall_other/2025-01-16_4.csv']
df = pd.concat([
pd.read_csv(fn) for fn in fnlist
])
# 이벤트로 260까진 금방 레벨업하므로 260 이상만 분석
df = df[df['character_level'] >= 260]
# 직업명 합치기
df['merged_class_name'] = df.apply(
lambda row: row['sub_class_name'] if pd.notna(row['sub_class_name']) else row['class_name'], axis=1
)
- 직업 점유율 시각화
분석의 결과는 숫자지만 시각화를 해야 해석 및 이해가 편합니다.
위에서 구한 값으로 간단한 시각화를 합시다.
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
"""
groupby('merged_class_name').size()를 하면 같은 이름인 size(수)를 반환합니다.
즉 직업별 유저수를 계산합니다.
sort_values(ascending=False)를 하면 내림차순 정렬로
가장 점유율이 많은 직업 순으로 저장됩니다.
"""
dd = df.groupby('merged_class_name').size().sort_values(ascending=False)
"""
dd는 pandas series 타입으로 index는 직업명, values는 직업의 유저수입니다.
"""
yLabels = dd.index
yValues = dd.values
yPositions = range(len(yValues))
fig = plt.figure(figsize=(6, 10))
ax = fig.add_subplot()
# x축은 유저수, y축을 직업명
ax.barh(yPositions, yValues)
# 한글을 쓰기 위한 폰트를 지정합니다. 메이플스토리 분석이라 메이플스토리 폰트를 씁시다.
fn_font = '../MaplestoryFont_TTF/Maplestory Light.ttf'
fontprop = fm.FontProperties(fname=fn_font, size=10)
# 직업 비율을 글자로 쓰는 부분입니다.
dd_sum = dd.sum()
for i, yValue in enumerate(yValues):
ax.text(yValue+1500, i, f'{round(yValue / dd_sum*100, 2)}%', fontsize=10, va='center', ha='center', fontproperties=fontprop);
# x축, y축 범위 지정
ax.set_xlim(0, 30000)
ax.set_ylim(-0.5, len(yValues)-0.5)
# x축 라벨, y축 라벨 설정
ax.set_xticks(range(0, 30000, 5000))
ax.set_xticklabels(range(0, 30000, 5000))
ax.set_yticks(yPositions)
ax.set_yticklabels(yLabels, fontproperties=fontprop)
"""
y축을 뒤집습니다. y=0 위치에 가장 유저수가 많은 직업이 표시되므로 뒤집어야 하며,
뒤집는 게 싫으시면 위에서 오름차순 정렬로 바꾸고
yPositions 변수값과 ax.text의 y을 그에 맞게 수정하시면 될겁니다.
"""
ax.invert_yaxis()
plt.show()
- 해석
챌린저스 월드는 2024년 12월에 열렸고, 2025년 4월에 2분화 및 밸런스 패치가 있었습니다.
그러므로 2024년 12월을 기준으로 직업 점유율을 해석해야합니다.
먼저 1~5등을 봅시다.
1등인 보우마스터는 조작법이 간단하지만 딜을 중상위권이어서 인기가 많았습니다.
2등 데몬어벤져와 5등 제논은 딜이 쌔기 때문에 사람들이 많이 했고요.
3등 윈드브레이커, 4등 은월은 유틸이 좋고 딜은 그럭저럭 괜찮아서 인기가 좋았습니다.
보우마스터, 윈드브레이커, 은월은 조작이 쉽지만 그다지 약하지는 않아서, 데몬어벤져, 제논은 딜이 좋아서 인기가 많았습니다.
다음으로 6~10등을 봅시다.
6등 나이트워커는 이전부터 좋은 직업 아니냐는 의견이 종종 보였었고, 7등 스트라이커는 원래 엄청 안 좋은 직업이라는 인식 있었지만 여러 패치 이후 좋다는 의견이 있었습니다.
8등 캡틴은 솔직히 왜 인기가 많았었는지 잘 모르겠습니다...
제로는 당시 1티어 직업 '데제제'(데몬어벤져, 제논, 제로)의 하나라서 9등을 차지했습니다.
10등 썬콜은 사냥이 쉬워서 그런 게 아닐까 싶네요.
중간 등수에서는 특별한 해석을 하긴 어렵고, 하위권에는 약하다고 알려지고, 인식이 안 좋은 직업들이 주로 있습니다.
이 때 카데나, 블래스터는 쌔다는 평이 많았지만 조작이 어려워서 그런지 점유율이 낮습니다.
- 직업 점유율 시각화 VU (Visual Update)
직업 점유율 시각화 그림을 좀 더 이쁘게 그려보았습니다.
그림 그리는 코드는 깃허브에 올려두긴 했는데 이미지 파일 같은 것은 따로 다운로드 받으셔야 합니다.
전 나무위키의 메이플스토리 직업 설명 페이지에 있는 이미지 파일을 썼습니다.
'프로젝트 > 메이플스토리 챌린저스 월드 시즌 1 분석' 카테고리의 다른 글
챌린저스 월드 시즌 1 데이터 분석: 3. 날짜, 레벨별 유저수 확인하기 (2) | 2025.06.07 |
---|---|
챌린저스 월드 시즌 1 데이터 분석: 1. 넥슨 Open API 데이터 다운로드 (0) | 2025.05.28 |
챌린저스 월드 시즌 1 데이터 분석: 0. 프롤로그 (0) | 2025.05.27 |