대부분의 회사에서는 여러 가지 현황을 파악하기 위해 다양한 지표를 집계합니다. 예를 들어 게임 회사의 경우 일별 접속 유저수 (Daily Active User, DAU), 일별 매출, 총 결제 유저수 (Paid User, PU), 유저별 평균 결제 금액 (Average Revenue Per User, ARPU) 등의 지표를 매일 혹은 매시간 단위로 집계하고 있습니다.
그런데 대부분의 지표들은 그 수치만으로는 정성적인 판단이 어렵습니다. 가령 'DAU 10만' 이라는 수치는 좋은걸까요 아니면 나쁜 걸까요? 그 대답은 그 게임이 어떤 게임이고 어떤 기대치를 갖고 있으며 기존에는 어땠는지 등의 맥락에 따라 달라집니다.
이런 맥락 파악을 위해 사용하는 대표적인 방법이 '비교'입니다. 즉, 유사한 다른 게임의 DAU와 비교하여 이 게임이 대박인지 쪽박인지 판단하거나 혹은 지난 주 DAU와 비교해서 현재 상승세인지 하락세인지를 가늠합니다.
특히 지표 모니터링 시에는 지표에 대한 시계열 그래프와 함께 '전일 대비 x % 상승' 혹은 '전주 대비 y % 하락' 등과 같이 과거 시점과의 비교 정보를 함께 제공합니다. 그러나 이런 식의 단순 비교는 몇 가지 문제가 있습니다.
첫째, 지표가 갖는 여러 가지 주기적 변화에 영향을 많이 받습니다. 예를 들어 주말에 주로 사람들이 많이 접속하는 게임의 경우 월요일의 DAU는 항상 일요일보다 낮게 나오고 반대로 주말의 DAU는 항상 좋게 나올 것입니다. 따라서 대부분의 경우 이런 문제를 피하기 위해 주기성을 고려하여 비교합니다. 가령 일주일 주기에 영향을 많이 받는다면 DAU 비교 시 전일 지표와의 비교 뿐만 아니라 전주 대비 변화도 같이 확인하면 됩니다.
그러나 대개의 경우 지표는 특정 하나의 주기에 영향을 받기 보다는 다양한 주기(시, 일, 주, 달, 분기, 년도)에 영향을 받습니다. 예를 들어 보통 온라인 게임에서는 '월초 효과'라는 것이 있어서 매월 초 매출이 가장 높습니다. 설날이나 입학/졸업 혹은 방학 역시 게임 지표에 영향을 주는 요소입니다.
따라서 좀 더 세밀한 비교를 위해 이런 것들을 모두 고려하여 전날, 전주, 전달, 전분기 등 다양한 주기의 과거 시점 지표와 비교하기도 합니다. 하지만 이런 경우 지표에 대한 해석은 더 어려워집니다. 예를 들어 어제 게임을 업데이트한 후 지표에 미친 영향을 보고 싶은데 하필 어제가 주말이었고 이번 주는 월초인데 지난 달은 방학이었다면 게임 업데이트가 지표에 어떤 영향을 줬는지 파악하기란 매우 어려울 것입니다. 다시 말해 만약 DAU가 전날보다는 하락했고 전주보다는 상승했는데 전달보다는 하락했으면서 전분기보다는 높아졌다면 이건 좋은 걸까요 아니면 나쁜 걸까요? 결국 비교 대상이 많아지고 각 대상이 일치된 결과를 주지 못한다면 그런 비교는 판단에 도움을 주기는 커녕 혼란만 더할 뿐입니다.
둘째, 특정 이벤트 효과가 지표 해석에 방해를 주기 쉽습니다. 예를 들어 특정 이벤트가 대박이 나서 이로 인해 매출이 큰 폭으로 늘었다면 해당 이벤트가 종료되고 난 이후 당분간은 지표가 항상 하락으로 표시될 것이고 이런 일관된 부정적인 정보는 담당자의 감각을 무디게 만들어 실제 긍정적인 효과를 줬던 이벤트나 컨텐츠가 무엇이었는지 눈치채지 못하게 만듭니다. 강한 빛이 비추고 난 이후에는 모든 게 깜깜하게 보이는 법입니다.
셋째, 심지어 여러가지 이벤트가 겹쳐서 발생할 경우 문제는 더 복잡해집니다. 보통 대규모 프로모션을 진행할 경우 이번과 비슷한 과거 프로모션 시점의 지표와 비교합니다. 또는 대규모 컨텐츠 업데이트를 할 경우 업데이트 효과를 분석하기 위해 업데이트 전/후 지표를 비교합니다. 그런데 게임에서는 보통 여러 개의 이벤트가 동시에 진행되기 때문에 특정 업데이트나 프로모션이 지표에 미치는 영향을 측정하기 어렵습니다. 예를 들어, 대규모 컨텐츠 업데이트 후 DAU가 크게 늘었을 때 이게 업데이트에 대해 유저가 만족해서 생긴 현상인지 아니면 동시에 진행 중인 복귀 유저 프로모션 때문인 것인지 알 수 없는 것이죠.
이 글에서는 이런 문제를 극복하고 지표를 좀 더 객관적으로 해석하기 위해 회귀 모델을 이용하는 방법을 제안합니다. 제안 방법의 목표는 다음과 같습니다.
1) 현재 지표의 높고 낮음을 판단하기 위한 객관적인 판단 기준 제시하기
2) 현재 진행되는 다양한 프로모션 및 업데이트가 지표에 미치는 효과를 각각 분리해서 추정하기
3) 지표의 이상 여부 파악하기
참고로 여기서 제안하는 방법은 검증된 기법은 아니고 말 그대로 제가 제안하는 방법입니다. 따라서 내용 상에 오류나 보완점에 대해선 댓글로 편하게 의견 주시면 좋겠습니다.
선형 회귀 분석은 예측 변수(혹은 종속 변수라고도 함)의 값에 영향을 주는 변수들 각각의 영향력을 추정하여 다음과 같은 회귀식을 만드는 기법입니다.
y = beta0 + beta1*x1 + beta2*x2 + beta3*x3 + ...
이 때 내가 분석하려는 지표를 y로 두고 이 지표에 영향을 주는 여러 가지 요소들(위에서 언급한 일, 주, 달 등의 시간 주기 및 이벤트나 업데이트 진행 여부)을 독립 변수(위 식에서 x1, x2, x3, ...)로 둔 후 회귀 모델을 만들면 각 변수의 회귀 계수는 각 변수가 지표에 미치는 영향력을 의미하게 됩니다.
예를 들어 아래와 같은 1년치 지표가 있다고 해보죠(이 글에서 사용하는 데이터는 실제 데이터가 아니고 제가 인위적으로 만든 자료입니다).
언뜻 보기에도 계절에 따른 주기성이 있는 것 같고 이벤트가 있었던 날의 지표가 아닌 날에 비해 전반적으로 높아 보입니다. 따라서 이런 경우 위와 같이 단순히 과거 지표와의 변화만 모니터링해서는 지표의 좋고 나쁨을 판단하기 어렵습니다. 이제 위에서 제안한 회귀 분석을 위해 지표에 영향을 줄만한 요인들을 뽑아 독립변수를 생성합니다. 여기서는 다음과 같은 요소를 정의했습니다.
월: 1~12월
요일: 월~일요일
이벤트1~4: 게임에 적용한 다양한 이벤트들을 비슷한 유형끼리 4개 카테고리로 나눈 후 각 유형의 이벤트가 있었던 날은 그 이벤트에 해당하는 변수에 1을, 아니면 0을 표기
이렇게 생성한 데이터의 예는 아래 그림과 같습니다.
이제 위 데이터를 이용해서 아래와 같이 회귀 모델을 생성합니다.
# 월과 요일을 factor 형으로 만드는 이유는
# 이렇게 하면 R에서 선형 회귀 모델링 시 자동으로 더미 변수를 생성해 주기 때문입니다.
data$month <- as.factor(month(data$date))
data$wday <- as.factor(weekdays(as.Date(data$date)))
model <- lm(dau ~ month+wday+event1+event2+event3+event4, data)
이 회귀 모델의 결과는 아래와 같습니다.
summary(model)
Call:
lm(formula = dau ~ month + wday + event1 + event2 + event3 +
event4, data = data)
Residuals:
Min 1Q Median 3Q Max
-2683.0 -698.6 -36.7 682.2 2629.0
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 58975.47 231.56 254.684 <2e-16 ***
month2 187.37 273.27 0.686 0.493
month3 -7584.61 266.56 -28.453 <2e-16 ***
month4 -7159.08 268.31 -26.682 <2e-16 ***
month5 -7673.25 266.22 -28.823 <2e-16 ***
month6 -7618.63 274.98 -27.706 <2e-16 ***
month7 -154.90 271.30 -0.571 0.568
month8 -203.52 266.23 -0.764 0.445
month9 -7601.16 268.62 -28.297 <2e-16 ***
month10 -6972.23 266.09 -26.203 <2e-16 ***
month11 -7560.89 268.16 -28.195 <2e-16 ***
month12 -42.94 266.85 -0.161 0.872
wday목요일 -308.42 204.04 -1.512 0.132
wday수요일 31.87 205.72 0.155 0.877
wday월요일 -521.30 203.99 -2.555 0.011 *
wday일요일 5141.59 206.66 24.880 <2e-16 ***
wday토요일 5174.91 203.68 25.407 <2e-16 ***
wday화요일 -271.37 204.39 -1.328 0.185
event1 3258.93 155.97 20.895 <2e-16 ***
event2 12117.28 438.01 27.665 <2e-16 ***
event3 3578.12 179.43 19.942 <2e-16 ***
event4 10121.93 154.26 65.616 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1043 on 344 degrees of freedom
Multiple R-squared: 0.9736, Adjusted R-squared: 0.972
F-statistic: 604.6 on 21 and 344 DF, p-value: < 2.2e-16
이제 이 모델을 이용해 지표를 해석하는 방법은 다음과 같습니다.
위 모델에서 가장 위에 빨간색으로 표시한 intercept는 종속 변수(즉, DAU)에 영향을 주는 요소들의 영향력을 모두 제거했을 때의 기본 수치를 의미합니다. 다시 말해 월이나 요일, 이벤트 등이 DAU에 주는 영향력이 있을 텐데 그런 것들을 모두 제거했을 때의 DAU가 얼마인지를 추정한 값입니다. 따라서 이것은 처음에 언급했던 지표의 높고 낮음을 판단하기 위한 기준값으로 사용할 수 있을 것입니다.
참고로 모든 회귀 모델에서 intercept가 의미가 있거나 위와 같이 해석할 수 있는 것은 아닙니다. 사실 대부분의 경우 회귀 모델을 데이터 해석에 사용할 때 intercept는 무시합니다. 그러나 위 경우라면 intercept는 해석이 가능합니다. 왜냐하면 제가 사용한 회귀 모델의 독립 변수들은 모두 명백한 의미를 가진 더미 변수들(dummy variables)이고 이들의 영향력을 모두 제거한 상황이 현실적으로 가능한 상황이기 때문이죠. 다시 말해 intercept는 '어떤 이벤트도 하지 않은 상태를 가정했을 때 1월달에 있는 금요일 DAU의 평균치'를 의미합니다. 왜 '1월달에 있는 금요일'이냐면 위 모델을 만들 때 '월'과 '요일' 더미 변수는 각각 '1월'과 '금요일'을 default 값으로 사용했기 때문입니다(모델 결과를 자세히 보시면 1월과 금요일에 해당하는 더미 변수가 없습니다). 따라서 월과 요일의 default 값을 무엇으로 하느냐에 따라 intercept값은 달라지게 됩니다. 그러면 이제 어떤 값을 default 값으로 할 것인지가 고민이 되는데, 이건 아마도 해당 지표의 해석을 담당하는 사람 혹은 의사 결정권자가 정해야 할 문제 같습니다.
암튼 원래의 지표 그래프에 위 intercept를 표시(파란색 수평선)한 그림은 아래와 같습니다. 이제 지표의 높고 낮음은 이 기준선을 이용해서 객관적으로 비교할 수 있습니다.
위 회귀 모델 결과에서 밑에 있는 event1~4 변수의 회귀 계수는 각 이벤트들이 DAU에 미치는 영향력을 의미합니다. 예를 들어 event1 이 있었던 날은 DAU 가 평균적으로 약 3,258 정도 상승한 것이죠. 이제 앞서 예로 들었던, 복귀 유저 프로모션과 동시에 진행되는 바람에 알 수 없었던 게임 업데이트가 지표에 미치는 영향도 추정이 가능합니다. 만약 복귀 유저 프로모션이 event4라고 하고 게임 업데이트가 있었던 날이 2016년 12월 28일 수요일이었고 복귀 유저 프로모션 외에 다른 이벤트는 없었다고 하면 우리는 해당 데이터를 이 회귀 모델에 넣어 DAU의 예측치를 얻을 수 있습니다(아래 코드 참고). 이제 우리는 이 값과 실제 DAU의 차이를 이번 업데이트가 DAU에 미친 영향력이라고 추정할 수 있습니다.
> real.dau <- 75345
> temp
date month wday event1 event2 event3 event4
363 2016-12-28 12 수요일 0 0 0 1
> pred <- predict(model, temp, type='response')
> real.dau - pred
6276.694 # 게임 업데이트가 DAU에 미친 영향력 추정치
참고로 구글이 만든 causal impact라는 라이브러리(https://google.github.io/CausalImpact/CausalImpact.html)가 이와 비슷한 컨셉을 갖고 있습니다. 물론 여기 제가 사용한 방법보다 훨씬 더 세련되고 정교한 기법을 사용했지만요. 예를 들어 아래 그래프는 causal impact 가 보여주는 그래프인데 위에서 부터 각각 실측치(실선)와 예측치(점선)을 비교한 그래프, 이 두 값의 차이를 표시한 그래프, 그리고 이 값의 누적치를 표시한 그래프입니다. 이 누적치가 바로 새로운 이벤트의 영향력을 의미합니다.
이제 마지막으로 바로 위에서 사용한 방법을 조금만 변형하면, 우리가 모르는 어떤 이상 현상을 찾는데에도 활용할 수 있습니다. 별다른 이벤트나 업데이트 같은 것이 없었는데도 회귀 모델의 예측치와 실제 DAU가 갑자기 크게 차이가 났다면 이것은 뭔가 이상 현상이 발생한 것이라고 의심할 수 있는 것이죠. 이와 관련해서는 예전에 제가 쓴 '회귀 분석을 이용한 시계열 데이터 이상 탐지(https://brunch.co.kr/@gimmesilver/4)' 라는 글에 좀 더 자세히 나와 있으니 참고하시면 되겠습니다.
여기서 끝이 아닙니다. 우리는 지표를 주기적으로 계속 모니터링하듯이 회귀 모델 역시 주기적으로 생성할 수 있습니다. 위 경우를 예로 들면, 한달에 한번씩 과거 1년치 데이터에 대한 회귀 모델을 정기적으로 생성하여 그 값들을 비교해 볼 수 있습니다. 이렇게 하면 우리는 DAU에 영향을 주는 변수들의 영향력 자체가 어떻게 변하는지를 추적할 수 있습니다. 가령 원래는 주말(토/일요일)에 평균적으로 DAU가 5000명 정도씩 늘어나고 있었는데 시간이 지남에 따라 영향력이 4000명으로 줄어든다면 이것은 중요한 정보가 될 수 있습니다. 혹은 event1 이 예전에 비해 영향력이 늘어나고 event2의 영향력은 떨어진다면 우리는 앞으로 event2보다 event1에 더 집중하는 것이 좋겠다는 의사 결정을 할 수도 있습니다.
더 나아가 기준선이 되는 intercept의 변화를 추적하는 것도 가능합니다. 그리고 이 intercept의 변화는 매우 중요할 수 있습니다. 이건 외부 영향력을 배제한 기본 수치를 의미하기 때문에 일종의 우리 서비스가 갖고 있는 잠재력이라고 생각할 수 있습니다. 따라서 장기적으로 볼 때 이 수치의 변화는 단기적인 DAU의 변화보다 더 중요한 정보일 수 있습니다.
'측정할 수 없는 것은 관리할 수 없다.'라는 말이 있습니다(흔히 피터 드러커가 한 말이라고 알려져 있는데 그건 아니라고 합니다). 서비스를 잘 운영하고 개선하기 위해 뭔가를 측정하고 집계해서 지표를 만드는 것은 중요합니다. 그런데 이렇게 다양한 지표들을 만들고 매일 바라보고 있지만 정작 이걸로 어떻게 하면 좋을지는 여전히 난감한 경우가 많습니다.
저희 회사에서도 수백 개가 넘는 지표를 매일 만들어서 다양한 그래프나 보고서로 제공하고 있지만, 지금 상황이 좋은지, 좋다면 대체 얼마나 좋은지를 해석하기가 쉽지 않습니다. 그 이유는 서두에서도 언급했듯이 단순한 수치만으로는 가치 평가를 하기 어렵기 때문입니다. 따라서 지표들이 좀 더 가치를 발휘하려면 현재의 지표가 좋은 상태인지 나쁜 상태인지를 알 수 있는 신뢰도 높은 비교 정보를 제시하는 것이 필요하다고 생각합니다. 비록 여기서 제안한 방법이 정답은 아니겠지만 적어도 정답을 찾기 위한 과정 중 하나라고 생각하며 혹시 더 좋은 의견이나 정보가 있다면 댓글로 알려 주시기 바랍니다.