챌린저스 월드 시즌 1 데이터 분석: 3. 날짜, 레벨별 유저수 확인하기

2025. 6. 7. 11:14·프로젝트/메이플스토리 챌린저스 월드 시즌 1 분석

- 개요

챌린저스 월드는 모두 처음부터 육성을 시작합니다.

이렇게 처음 서버가 열리면 경쟁을 하기 마련이죠.

경쟁요소로는 흔히 퍼클이라 불리는 특정 보스의 첫 클리어와 레벨 순위가 있습니다.

메이플스토리의 역사가 오래된 만큼 지금까지 최고레벨이 200부터 250, 275, 300까지 확장되었고 최고레벨이 확장될 때마다 최고 레벨 달성 경쟁이 있었습니다.

그래서 저는 챌린저스 서버 레벨 순위의 의미가 적지는 않다고 생각합니다.

사실 저만해도 일주일 동안 제 직업순위를 확인하곤 했거든요.

 

이번 포스트에서는 순위와 연관이 있는 날짜, 레벨별 유저수를 확인하고자 합니다.

 

- 데이터 특징

모든 유저의 정보를 다운로드 받을 수 없으므로 레벨 260이상 유저를 대상으로 분석합니다.

챌린저스 1 서버에서 1등부터 15만등까지의 데이터를 다운로드 받았습니다.

제가 확인해본 결과, 15만등 유저의 레벨은 항상 260보다 낮으므로 챌린저스 1 서버의 모든 260 이상 유저를 분석할 수 있었습니다(분석기간 내 15만등 유저의 최고레벨은 235이고 1등의 최고레벨은 289입니다.). 

 

- 날짜별 레벨별 유저수: 누적 막대그래프

날짜별로 260~264, 265~269처럼 5단위 레벨 구간을 정하여 누적 막대그래프를 그리겠습니다.

메이플스토리는 260부터 레벨 5단위로 새로운 지역에 입장 가능하고, 레벨 보상으로 얻는 챌린저스 점수의 기준도 레벨 260부터 5단위였습니다.

다음은 데이터 처리 과정 코드입니다.

"""
데이터 읽기
모든 파일을 하나의 폴더에 csv 형태로 저장
일부 중복되는 파일이 있어 drop_duplicates()로 중복 제거
"""
import pandas as pd
import os
inpath = "../data/chall_rank_more/"
fnlist = os.listdir(inpath)
dfs = []
for fn in fnlist:
    df = pd.read_csv(inpath+fn)
    dfs.append(df)
data = pd.concat(dfs)
data.drop_duplicates(inplace=True)


"""
character_level_group이라는 칼럼명으로 5단위로 레벨 구간 부여
// 연산자는 나누기의 몫만 남김
260을 뺀 다음 5로 나눈 몫만 남기므로
260~264는 0, 265~269는 1, 270~274는 2...와 같은 결과가 나옴
groupby를 date, character_level_group 칼럼에 적용하고 size는 그룹에 속한 행의 수를 반환
date별 character_level_group에 속하는 유저의 수를 저장함
"""
data['character_level_group'] = (data['character_level'] - 260 ) // 5
data2 = data[data['character_level_group'] >= 0].groupby(by=['date','character_level_group']).size()

"""
groupby를 수행하면 date와 character_level_group 칼럼은 index 형태가 됨
reset_index()를 하면 index가 칼럼 형태의 데이터로 변환
name='size'의 의미는 위의 groupby().size()로 구한 유저수를 size라는 칼럼명으로 지정
최종적으로 data2에는 date, character_level_group, size라는 칼럼명으로 값들이 저장됨
"""
data2 = data2.reset_index(name='size')
data2['date'] = pd.to_datetime(data2['date'])

 

위에서 계산한 자료로 누적 막대그래프를 그려봅시다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
sdate = '2024-12-20'
edate = '2025-05-22'
base_date = pd.to_datetime(sdate)

xx = pd.date_range(sdate, edate, freq='D')
yy = np.zeros((len(xx), 6))
for idx, row in data2.iterrows():
    yy[(row['date'] - base_date).days , row['character_level_group']] = row['size']

colors = [
    '#ff9999',  # 연분홍
    '#66b3ff',  # 하늘색
    '#99ff99',  # 연녹색
    '#ffcc99',  # 살구색
    '#c2c2f0',  # 연보라
    '#ffd700',  # 골드
]

label_str = ['260-264', '265-269', '270-274',  '275-280', '280-284', '285-289']
bottom = np.zeros(yy.shape[0])

fig, ax = plt.subplots(figsize=(12, 6))
for i in range(6):
    ax.bar(xx, yy[:, i], bottom=bottom, color=colors[i], label=label_str[i]); # bottom값부터 막대그래프를 그림
    bottom += yy[:, i]
ax.set_xticks(xx[0::7]);
ax.set_xticklabels((xx[::7]-pd.Timedelta('1D')).strftime('%Y-%m-%d'), rotation=90, fontsize=12);
ax.set_xlim(pd.to_datetime(sdate) - pd.Timedelta('1D'), pd.to_datetime(edate) + pd.Timedelta('1D'))

ax.set_yticks(range(0, 120000, 30000))
ax.set_yticklabels(range(0, 120000, 30000), fontsize=12)
ax.set_ylim(0, 110000)
ax.legend(fontsize=12)

 

이렇게 누적 막대그래프를 그리면 막대의 길이는 레벨 260 이상 유저수가 됩니다.

레벨 260 이상 유저수는 2025년 2월까진 증가하고, 3월 중하순까지 유지된 후 감소합니다.

챌린저스 서버의 유저는 2025년 1월 16일부터 일반 서버로 리프할 수 있으므로, 레벨 260을 달성하는 유저수보다 리프하는 유저가 많으면 유저수가 감소합니다.

 

- 날짜별 레벨별 유저수: 레벨 그룹별 시계열

누적 막대그래프는 모든 레벨 그룹의 유저수를 한 눈에 확인하기 좋습니다.

그런데 각각 그룹별 유저수를 확인할 때 문제가 있습니다.

가장 아래 막대인 260-264 유저수의 변화는 쉽게 확인할 수 있는 반면, 그 위에 쌓인 더 높은 레벨 그룹의 막대 길이 변화를 가늠하기 어렵습니다.

특정 레벨 그룹의 유저수 변화를 살펴보려면 누적 막대그래프가 아니라 다른 형태로 시각화해야합니다.

 

아래는 레벨 그룹별 시계열을 그리는 코드입니다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
sdate = '2024-12-20'
edate = '2025-05-22'
base_date = pd.to_datetime(sdate)

xx = pd.date_range(sdate, edate, freq='D')
yy = np.zeros((len(xx), 6))
for idx, row in data2.iterrows():
    yy[(row['date'] - base_date).days , row['character_level_group']] = row['size']

colors = [
    '#ff9999',  # 연분홍
    '#66b3ff',  # 하늘색
    '#99ff99',  # 연녹색
    '#ffcc99',  # 살구색
    '#c2c2f0',  # 연보라
    '#ffd700',  # 골드
]
label_str = ['260-264', '265-269', '270-274',  '275-280', '280-284', '285-289']
yticks = [
    range(0, 60000+1, 20000),
    range(0, 1500+1, 500),
    range(0, 20+1, 10)
]

fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(12, 10))
fig.subplots_adjust(hspace=0.05)  # 세로 간격 줄이기

for i in range(6):
    if i < 4:
        axes[0].plot(xx, yy[:, i], color=colors[i], label=label_str[i])
    else:
        axes[i-3].plot(xx, yy[:, i], color=colors[i], label=label_str[i])

xticklabels = [
    [],
    [],
    (xx[::7]-pd.Timedelta('1D')).strftime('%Y-%m-%d')
]

for i in range(3):
    axes[i].set_xticks(xx[0::7]);
    axes[i].set_xlim(pd.to_datetime(sdate) - pd.Timedelta('1D'), pd.to_datetime(edate) + pd.Timedelta('1D'))
    axes[i].set_xticklabels(xticklabels[i], rotation=90, fontsize=12);
    axes[i].set_yticks(yticks[i])
    axes[i].tick_params(axis='y', labelsize=12)
    axes[i].legend(fontsize=12)
    for x in xx[0::7]:
        axes[i].axvline(x = x, lw=0.5, color='black', alpha=0.2)

 

레벨 그룹별 유저수 크기에 따라 시계열을 3개 그렸습니다.

흥미로운 점으로는 

레벨 260-264의 최대 유저수 날짜는 1월 15일,

레벨 265-269의 최대 유저수 날짜는 1월 29일(이전 그룹과 14일 차이),

레벨 270-274의 최대 유저수 날짜는 2월 19일(이전 그룹과 약 21일 차이),

레벨 275-279의 최대 유저수 날짜는 4월 13일(이전 그룹과 약 53일 차이)이라는 것입니다.

 

이 말은 곧 챌린저스 서버의 다수는 위와 같은 시나리오로 레벨업을 한다는 것입니다.

기획자 입장에서는 다음 챌린저스 서버를 기획할 때 이런 사실을 참고하면 좋겠죠.

다만 챌린저스 서버에는 리프가 있으므로 기획자 관점에서는 유저마다 특정 레벨에 도달한 날짜를 구한 뒤 이 값을 누적하는 방식으로 계산하여 시각화하는 것이 좋을겁니다.

 

레벨 280-284, 285-289 구간의 최대 유저수 날짜는 둘 다 4월 24일 즈음인데 레벨업에 의미를 둔 아웃라이더 유저라고 생각하는 것이 타당한 듯합니다.

 

- 다음 챌린저스 서버에서 1등을 하려면?

이 글을 작성하고 있는 오늘이 바로 2025년 6월 7일 메이플스토리 여름 쇼케이스 날입니다.

아마 6월 19일에 챌린저스 월드 시즌2가 열리겠고, 레벨 경쟁이 있겠죠.

챌린저스 월드 시즌1을 기준으로 어느 정도 속도로 레벨업 해야 랭킹권인지 알아볼 수 있습니다.

제 기억에 챌린저스 월드 시즌1이 출시된 이후 각종 경험치 컨텐츠가 추가되어 실제로는 더 빨리 레벨업 해야합니다.

 

챌린저스 월드 시즌1, 1등의 유저 데이터를 보면

1등 유저는 2024년 12월 19일인 첫날에 레벨 261,

레벨 265를 2024년 12월 21일 (3일차),

레벨 270을 2024년 12월 23일 (5일차),

레벨 275를 2024년 12월 27일 (9일차),

레벨 280을 2025년 1월 8일 (21일차),

레벨 285를 2025년 2월 9일 (52일차)에 달성합니다.

 

이번 챌린저스 월드가 6월 19일에 열린다고 치고, 시즌 1 랭킹 1등 유저의 레벨 시나리오대로라면 아래와 같은 스케줄이 나옵니다.

레벨 265: 6월 21일

레벨 270: 6월 23일

레벨 275: 6월 27일

레벨 280: 7월 9일

레벨 285: 8월 9일

'프로젝트 > 메이플스토리 챌린저스 월드 시즌 1 분석' 카테고리의 다른 글

챌린저스 월드 시즌 1 데이터 분석: 2. 직업 점유율 시각화  (0) 2025.05.30
챌린저스 월드 시즌 1 데이터 분석: 1. 넥슨 Open API 데이터 다운로드  (0) 2025.05.28
챌린저스 월드 시즌 1 데이터 분석: 0. 프롤로그  (0) 2025.05.27
'프로젝트/메이플스토리 챌린저스 월드 시즌 1 분석' 카테고리의 다른 글
  • 챌린저스 월드 시즌 1 데이터 분석: 2. 직업 점유율 시각화
  • 챌린저스 월드 시즌 1 데이터 분석: 1. 넥슨 Open API 데이터 다운로드
  • 챌린저스 월드 시즌 1 데이터 분석: 0. 프롤로그
레까
레까
  • 레까
    데이터 조아
    레까
  • 전체
    오늘
    어제
    • 전체 (98)
      • 일기장 (0)
      • 대기과학 (49)
        • 프로그래밍 (45)
        • 개념 (2)
        • 칼럼 (2)
      • 여러가지 데이터 (5)
        • 프로그래밍 & 분석 (5)
      • 프로그래밍 (18)
        • 파이썬 (8)
        • 시각화 (9)
        • 유용 (1)
      • 프로젝트 (21)
        • 기계학습 기반 서울 기온 예측 (9)
        • 사과게임 매크로 만들기 (4)
        • 버스 한 번으로 특정 지역에 갈 수 있는 지역 찾.. (4)
        • 메이플스토리 챌린저스 월드 시즌 1 분석 (4)
      • 데이터리안 SQL 공부 (4)
      • 주제별 링크 모음 (1)
      • 백업 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
레까
챌린저스 월드 시즌 1 데이터 분석: 3. 날짜, 레벨별 유저수 확인하기
상단으로

티스토리툴바