데이터로그😎
[데이터리안_플젝1] 유저 인게이지먼트 하락 원인 분석 ② 본문
🚨 *데이터리안의 무료 강의를 참고했습니다. Mode에서 제공하는 소스코드는 제가 사용하는 MySQL에는 맞지않아, MySQL에 맞도록 코드를 직접 짰습니다. 따라서 문제점이 발견될 수 있습니다. 수정 사항을 발견하시면 언제든 댓글 주세요. 분석에 사용한 자료들의 출처와 상세 코드는 맨아래출처 부분에서 확인할 수 있습니다.
*데이터리안 강의
이전 단계
2024.01.01 - [SQL/프로젝트] - [데이터리안_플젝1] 유저 인게이지먼트 하락 원인 분석 ①
개요
문제 상황 |
*Yammer의 활성유저 대시보드를 확인해보니 **주간 유저 활동 수가 점차 줄고 있다. * Yammer: 기업용 소셜 네트워크 서비스이다. 사내 페이스북이라고 볼 수 있다. ** 주간 유저 활동 수: 한 주간 Yammer에서 활동한 유저 수이다. 앞으로 ***WAU (Weekly Active User)라고 칭하겠다. *** DAU(Daily Active User), WAU(Weekly Active User), MAU(Monthly Active User) |
분석 목적 |
WAU가 감소하는 원인을 찾아야 한다! |
데이터 |
사용할 테이블은 총 3개 (emails, events, users) |
분석 방식 |
Python으로 MySQL 데이터를 불러와서 분석을 진행할 것이다. 최대한 MySQL 쿼리문만을 사용할 것이며, 부득이한 경우 파이썬 코드를 추가하겠다. |
분석 준비
1. python - MySQL 연결함수 생성
- create_mysql_connection : 맨 처음 python - mysql의 connection을 형성할 때 한 번만 사용
- close_connection : 분석이 끝난 후 connection을 끊기위해 사용
import mysql.connector
from mysql.connector import connect,Error
import pandas as pd
# mysql 연결 함수
def create_mysql_connection(host, user, password, database):
try:
connection = connect(
host = host,
user = user,
password = password,
database = database
)
if connection.is_connected():
print(f'Connected to MySQL server : {host}')
return connection
except Error as e:
print(f'Error: {e}')
return None
# mysql 연결 종료 함수
def close_connection(connection):
if connection:
connection.close()
print('Connection to MySQL closed.')
2. connection 객체 생성
connection을 통해 CLOUDY라는 사용자로 yammers라는 데이터베이스에 접속하는 것이다.
connection = create_mysql_connection('localhost','CLOUDY','1234','yammers')
3. 테이블 확인
1. users : 유저 정보 (가입일, 사용언어, 계정 활성상태 등)
2. events : 유저 활동 정보 (활동 내용, 타입, 위치, 사용기기 등)
3. emails : 이메일 관련하여 발생한 활동
세개의 테이블 모두 user_id라는 공통된 컬럼을 가지고 있다.
4. 가설생성의 예시
WAU가 감소한 원인에는 무엇이 있을까? 매우 다양할 것이다. MODE 사이트에서 예로 들어준 가설들은 다음 같다.
Holiday | yammers는 사내 페이스북같은 기능이다. 따라서, 특정 국가가 다른 국가보다 훨씬 낮은 engagement를 갖고 있다면, 해당 국가가 공휴일일 수도 있다. |
Broken Feature | - 어플 기능이 고장나서 사람들이 사용하지 못한 것이 원인일 수도 있다. - 서비스 가입 기능 고장, 모바일 앱 고장 |
Broken Tracking Code |
- 로그를 수집하는 코드 문제 |
Traffic anomalies from bots |
- 대부분 웹사이트에서는 bot의 활동기록이 많다. - 제품이나 인프라가 변화해서 봇의 참여가 줄어들 수 있다. |
Traffic shutdown to your site |
구글, 네이버 등 internet service providers가 사이트를 차단했을 수도 있다. |
Marketing event | - 마케팅 이벤트가 순간적으로 engagement를 펌핑했다가 떨어진 것은 아닐까? - 예를 들면 superbowl 광고로 유입된 유저들은 서비스 이탈율이 높을 수도 있다. - 이럴 땐 marketing department 사람들에게 어떤 빅 마케팅이 있었는지 물어볼 수 있다. |
Bad data | - QA(Quality Assurance)데이터 (사내 트래킹)를 실제 유저 데이터와 분리하지 않은 상태인데, 만약 이번주부터 QA를 안하기로 했다면 WAU가 떨어질 수도 있다. |
Search Crawler changes | - 검색 엔진이 우리 서비스를 검색 키워드 몇위에 랭킹 시켜주는지가 서비스 트래픽을 결정할 수 있다 -> 따라서 랭킹로직이 변화되었다면 WAU 변경에 영향을 미칠 수 있다. |
*가설의 우선순위 세우는 법
이렇게 가설, 원인이 많을 땐 [우선순위]를 반드시 세워야 한다.
1. Experience: 경험이 중요하다. 비슷한 문제에 대한 경험자라면 우선순위를 잘 세울 수 있다.
2. Communication✳️: 관련 부서에게 물어볼 수 있다.
3. Speed: 빠르게 체크할 수 있는 것부터 해야한다. (익숙하거나, 잘 정제되어 있는 데이터라던가, 재활용할 수 있는 쿼리가 있던지..)
4. Dependency: 하나를 확인해보고, 이것과 관련되어 있어서 쉽게 확인할 수 있는 것 순으로 확인해봐라.
[분석1] User수 추이 파악
1. DAU (Daily Active User) 분석
- 각 날짜 별로 몇명의 user가 로그인을 했는지 알아볼 것이다.
# sql 실행 query = " SELECT DATE_FORMAT(occurred_at, '%y-%m-%d') AS DATE,\ COUNT(DISTINCT user_id) AS Daily_Active_Users \ FROM events \ WHERE event_type = 'engagement' \ AND event_name = 'login' \ GROUP BY 1 \ ORDER BY 1" DAU_df = pd.read_sql(query, connection) DAU_df # 그래프 import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(30,10)) sns.lineplot(data=DAU_df,x='DATE', y='Daily_Active_Users') plt.xticks(rotation=90) plt.show()
- 결론
- DAU 변화 그래프를 살펴보니 일정 패턴이 반복될 뿐, 특정 기간에 급격한 user의 감소가 발견되진 않았다.
- 반복적으로 DAU가 낮아지고 있는 구간은 주말인 경우이다. 사내 SNS이기 때문에 주말에는 활성화되지 않는다.
- 그래도 자세히 보면 2014-08월부터 조금씩 DAU가 감소하는 것이 보인다.
- DAU 변화 패턴을 보기 위해 Week 별로 데이터를 묶어볼까?
2. WAU (Weekly Active User) 분석
- 먼저 Daily를 Weekly로 변환해야 한다. Weekly로 변환하는 기준은 event가 발생한 날짜가 속한 주의 월요일로 하겠다. 한 주는 월~일이라고 정한다. 이해가 되지 않는다면 아래의 그림을 참고하시길.
- 만들고자 하는 표는 "한 주에 yammer에서 활동한 user수" 를 구하는 것이다. 이에 더해 user수 변화율도 구하겠다.
# sql query = " SELECT *, ROUND((WAU-LAG(WAU) OVER())/LAG(WAU) OVER()*100,2) as WAU_GROWTH\ FROM (SELECT DATE_SUB(DATE_FORMAT(occurred_at, '%y-%m-%d'), INTERVAL WEEKDAY(occurred_at) DAY) start_week,\ COUNT(DISTINCT user_id) WAU \ FROM events \ WHERE event_type = 'engagement' \ GROUP BY 1 \ ORDER BY 1) A " wau_df = pd.read_sql(query, connection) wau_df # 그래프 plt.figure(figsize=(10,4)) sns.lineplot(data=wau_df, x='start_week', y='WAU', marker='o') plt.xticks(rotation=45) plt.title('WAU change') plt.show() plt.figure(figsize=(10,4)) sns.lineplot(data=wau_df,x='start_week', y='WAU_GROWTH', marker='o') plt.axhline(y=0, color='red', linestyle='--') plt.xticks(rotation=45) plt.title('WAU GROWTH RATE') plt.legend() plt.show()
- 결론
- WAU change 그래프를 보면 2014.7월 말 ~2014.8월 초에 WAU가 많이 감소했다.
- WAU GROWTH RATE 그래프를 보면 역시나 동일 기간에 WAU의 감소율이 가장 크다.
분석1의 결론
지금까지 DAU와 WAU를 살펴본 결과 2014-07말 ~ 2014-08초에 급격한 user engagement drop이 발생했다.
왜 이 시기에 user engagement의 drop이 발생했는지를 알아보아야 한다.
혹시 "신규 가입자"가 감소한 것이 원인은 아닐까?
'신규 가입자가 감소하여 engagement drop이 발생했다.' 를 가설로 설정해보자.
지금부터 `2014-06월 ~ 2014-09`월까지 발생한 로그들을 살펴보며 이 가설을 검증해보겠다.
[분석2] 신규 가입자 변화량 분석
1. 신규 가입자 수의 변화 분석 (Daily)
- 2014-06~ 2014-09월 사이에 신규 가입한 유저 수의 변화를 Daily로 살펴보자
# SQL query = " SELECT DATE_FORMAT(created_at, '%Y-%m-%d') AS day,\ COUNT(*) AS all_users,\ COUNT(CASE WHEN state ='active' THEN user_id ELSE NULL END) AS activated_users \ FROM users \ WHERE created_at >= '2014-06-01' AND created_at < '2014-09-01' \ GROUP BY day \ ORDER BY day;" user_df = pd.read_sql(query, connection) user_df # 그래프 import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(40,6)) sns.lineplot(x=user_df.day,y=user_df.activated_users, label='Active Users' ) sns.lineplot(x=user_df.day,y=user_df.all_users, label='All Users' ) plt.legend() plt.xticks(rotation=90) plt.show()
- 결론
- 전 기간에서 신규 가입자 수의 변화 패턴은 비슷하나, 7~8월 사이 신규 가입자의 수가 다른 기간에 비해 적다.
- 패턴 파악을 위해 Week 별로 묶어서 분석할 필요가 있다.
2. 신규 가입자 수의 변화 분석 (Weekly)
- 2014-06~ 2014-09월 사이에 신규 가입한 유저 수의 변화를 Weekly로 살펴보자
query = " SELECT *, \ (all_users-LAG(all_users) OVER())/LAG(all_users) OVER()*100 as all_users_growth, \ (activated_users-LAG(activated_users) OVER())/LAG(activated_users) OVER()*100 as activated_users_growth \ FROM \ (SELECT DATE_SUB(DATE_FORMAT(created_at, '%Y-%m-%d'), INTERVAL WEEKDAY(created_at) DAY) created_week,\ COUNT(*) as all_users,\ COUNT( CASE WHEN state='active' THEN user_id ELSE NULL END) as activated_users\ FROM users \ WHERE created_at >= '2014-06-01' AND created_at < '2014-09-01' \ GROUP BY 1 \ ORDER BY 1) A" user_week_df = pd.read_sql(query, connection) user_week_df
- 결론
- Week별로 신규 가입자를 분석해보니 7~8월에 신규 유저 감소가 보인다.
- 신규 유저가 감소한 것도 7~8월 user engagement drop의 원인이라고 볼 수 있다. 신규 유저 유치를 위해 마케팅 전략을 짜는 것도 좋다.
분석2 결론
2014-07말 ~ 2014-08초에 급격한 user engagement drop이 발생했는데, 신규 가입자의 감소가 이러한 drop에 영향을 끼쳤다고 볼 수 있다. 그러나, 신규 가입자 외에 기존 user의 활동 감소도 user engagement drop에 영향을 끼치지 않았을까?
'기존 user의 활동량이 감소하여 engagement drop이 발생했다.' 를 가설로 설정해보자.
[분석3] 기존 User의 활동 분석 (코호트 분석)
코호트?
코호트 분석에 대한 설명은 이전 글을 참고하시길 바란다.
2023.12.14 - [통계] - [코호트 분석 1] 개념 & 실습
코호트 분석을 위한 쿼리문 설명
코호트 분석을 통해 기존 유저의 활동을 분석할 것이다. 이 때 코호트는 가입 기간에 따른 user group 이라고 정의하겠다. 코호트 분석을 위한 쿼리문은 아래와 같다.
### 1. user age 구하기, 주별로 묶기
query = " SELECT event_happened_week, cohort, COUNT(DISTINCT user_id) as user_cnt\
FROM \
(SELECT u.user_id,\
DATE_SUB(DATE_FORMAT(e.occurred_at, '%Y-%m-%d'), INTERVAL WEEKDAY(e.occurred_at) DAY) event_happened_week,\
CASE WHEN DATEDIFF('2014-09-01', u.created_at) >=70 THEN '10+ weeks'\
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 63 AND 69 THEN '9 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 56 AND 62 THEN '8 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 49 AND 55 THEN '7 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 42 AND 48 THEN '6 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 35 AND 41 THEN '5 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 28 AND 34 THEN '4 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 21 AND 27 THEN '3 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 14 AND 20 THEN '2 weeks' \
WHEN DATEDIFF('2014-09-01', u.created_at) BETWEEN 7 AND 13 THEN '1 week' \
ELSE 'less than a week' \
END AS 'cohort'\
FROM users u \
JOIN events e \
ON u.user_id = e.user_id \
AND e.event_type = 'engagement' \
AND e.event_name = 'login' \
AND e.occurred_at >= '2014-05-01' AND e.occurred_at < '2014-09-01' \
WHERE u.state = 'active') A\
GROUP BY event_happened_week, cohort \
ORDER BY event_happened_week"
cohort_df = pd.read_sql(query, connection)
cohort_df
내부 쿼리문 (FROM절의 서브 쿼리문)
- 코호트 분석을 위해 가입기간에 따라 user group을 나누기로 하였다. 이때 User의 가입기간은 2014.09월➖ user계정이 create된 일자 와 같은 식을 사용하여 구했다.
- CASE WHEN문을 사용하여 Cohort 를 아래와 같이 나누었다.
cohort 가입기간 10+ weeks 가입한지 10주 이상인 user 9 weeks 가입한지 9주 된 user ... ... 1 week 가입한지 1주 된 user less than a week 가입한지 1주 미만인 user
외부 쿼리문
event가 발생한 weeky별로, cohort 별로 group화 (GROUP BY) 하여 user engagement 수를 센다. 쿼리를 실행하여 나온 최종 테이블인 cohort_df는 아래와 같은 모양을 띈다.
Retention table
코호트 분석을 위해서는 표를 아래와 같은 retention table의 형식으로 변형할 필요가 있다. 즉, 컬럼에 week가, index엔 일자가 오는 형식의 표를 말한다. 변형을 위해서는 pivot 기능을 사용하면 편하다. pivot 기능은 mysql엔 없다고 한다. (Chat GPT에 따르면... 혹시나 있다면 알려주세요..) 그래서 python의 기능을 사용하여 표를 pivot할 것이다.
PIVOT
- 아래의 코드와 같이 데이터프레임.pivot() 함수를 사용하면 쉽게 데이터프레임을 피벗할 수 있다.
- 코드 아래에 있는 테이블은 최종적으로 pivot된 테이블이고, 이 테이블을 사용하여 그래프를 그릴 것이다.
# 'variable' 열을 행 인덱스로, 'value' 열을 새로운 열로 변환 pivot_df = cohort_df.pivot(index='event_happened_week', columns='week_cnt', values='user_cnt') pivot_df = pivot_df[[ '10+ weeks', '9 weeks','8 weeks','7 weeks','6 weeks','5 weeks','4 weeks','3 weeks', '2 weeks','1 week','less than a week']] # 결과 확인 pivot_df
그래프
plt.figure(figsize=(16,8))
plt.plot(pivot_df, label = pivot_df.columns, marker = 'o')
plt.xlabel('event_happened_week')
plt.ylabel('Weekly Active User Count')
plt.xticks(rotation=45, ha='right')
plt.legend(title='User groups')
plt.show()
분석3 결과
가입으로부터 시간이 지날 수록 Active User가 감소하는 것은 Retention 차트의 전형적인 패턴이다. less than a week ~ 9 weeks까지의 코호트는 일반적인 retention 차트의 특성을 보이며 Active User가 감소한다. 그러나, 10+ weeks 코호트를 주목해야 한다.
10+ weeks 코호트는 가입한지 10주 이상이 된 유저 그룹이다. 이 그룹에는 10주 전에 가입한 유저, 11주 전에 가입한 유저, 12주 전에 가입한 유저 등의 사용자가 모두 포함이 되어있다. 따라서 2014-06월 중순까지 활동이 꾸준히 상승한다(가입자가 늘고있기 때문에) . 그러나 이들은 2014-06-16일 즈음부터 활동이 줄어든다(일반적인 패턴) . 여기서 주목할 것은 2014-08월 이전까지 다시 사용량이 상승하다가 8월 이후 급격하게 감소한다는 것이다.
분석3의 결론은 "기존 유저들 중 가입한지 10주 이상이 된 유저로부터 독특한 WAU 감소가 나타난다" 는 것이다.즉, WAU의 감소가 마케팅 트래픽으로 인한 spike나 서치엔진의 랭킹 변화나 사이트 차단과 같은 new traffic에 영향을 주는 요소들과는 관계가 없다는 것이다. 그렇다면... 왜 기존 유저들 사이에서 이러한 WAU 감소가 나타났을까??
먼저 생각해보아야 하는 것은 기존 유저들이 yammer에 어떻게 유입되는가? 이다.
기존 유저들이 yammer에 유입되는 경로는 크게 세가지로 생각해볼 수 있겠다.
지금부터 아래의 원인들을 살펴볼 것이다.
- 컴퓨터에서 직접 유입 (분석4)
- 핸드폰, 태블릿의 어플로 유입 (분석4)
- yammer에서 보내는 email newsletter을 통한 유입 (분석5)
[분석4] 디바이스별 WAU 분석
# sql
query = " SELECT device_type, event_happened_week, COUNT(DISTINCT user_id) as user_cnt\
FROM \
(SELECT e.user_id, \
DATE_SUB(DATE_FORMAT(e.occurred_at, '%Y-%m-%d'), INTERVAL WEEKDAY(e.occurred_at) DAY) event_happened_week, \
CASE \
WHEN device IN ('macbook pro', 'lenovo thinkpad', 'macbook air', 'dell inspiron notebook', 'asus chromebook', 'dell inspiron desktop', 'acer aspire notebook', 'hp pavilion desktop', 'acer aspire desktop', 'mac mini') THEN 'computer' \
WHEN device IN ('iphone 5', 'samsung galaxy s4', 'nexus 5', 'iphone 5s', 'iphone 4s', 'nokia lumia 635', 'htc one', 'samsung galaxy note', 'amazon fire phone') THEN 'phone' \
WHEN device IN ('ipad air', 'nexus 7', 'ipad mini', 'nexus 10', 'kindle fire', 'windows surface', 'samsung galaxy tablet','samsumg galaxy tablet') THEN 'tablet'\
ELSE 'NONE'\
END AS 'device_type'\
FROM events e JOIN users u ON e.user_id = u.user_id\
AND e.event_type = 'engagement' AND e.event_name = 'login'\
AND e.occurred_at >= '2014-05-01' AND e.occurred_at < '2014-09-01' \
WHERE u.state = 'active') A\
GROUP BY device_type, event_happened_week ;"
device_df = pd.read_sql( query, connection)
# device_df를 pivot
device_pivot_df = device_df.pivot(index='event_happened_week', columns='device_type', values='user_cnt')
device_pivot_df
그래프 그리기
WAU 그래프
plt.figure(figsize=(10,8))
sns.lineplot(data=test_pivot_df)
sns.lineplot(data=wau_df, x='start_week', y='WAU', marker='o', color='green', label='WAU')
plt.xticks(rotation=45)
plt.show()
GROWTH 그래프
각 device의 WAU 증감율을 분석하기 위해 shift()라는 python의 함수를 사용했다.
growth_df = pd.DataFrame()
growth_df['computer_growth'] = (device_pivot_df['computer']-device_pivot_df['computer'].shift(1))/device_pivot_df['computer'].shift(1)*100
growth_df['phone_growth'] = (device_pivot_df['phone']-device_pivot_df['phone'].shift(1))/device_pivot_df['phone'].shift(1)*100
growth_df['tablet_growth'] = (device_pivot_df['tablet']-device_pivot_df['tablet'].shift(1))/device_pivot_df['tablet'].shift(1)*100
growth_df
plt.figure(figsize=(10,8))
sns.lineplot(data=growth_df, marker='o')
plt.xticks(rotation=45)
plt.show()
분석4 결론
7~8월에 computer, phone, tablet 모두 사용량이 감소하고 있으나, phone, tablet에서 WAU의 감소폭이 크다.
모바일 앱에 문제가 생긴 것이 원인일 수도 있다. 이럴 땐 유관 부서에 물어봐야 한다.
[분석5] email 분석
# sql
query = " SELECT type, event_happened_week, COUNT(DISTINCT user_id) as user_cnt \
FROM (SELECT *, CASE WHEN action = 'sent_weekly_digest' THEN 'weekly_emails' \
WHEN action = 'email_open' THEN 'email_opens' \
WHEN action = 'email_clickthrough' THEN 'email_clickthrough' \
WHEN action = 'sent_reengagement_email' THEN 'reengagement_emails' \
ELSE 'NONE' END AS type, \
DATE_SUB(DATE_FORMAT(occurred_at, '%Y-%m-%d') , INTERVAL WEEKDAY(occurred_at) DAY ) 'event_happened_week'\
FROM emails)A \
GROUP BY type, event_happened_week"
email_df = pd.read_sql(query,connection)
# eamil_df를 pivot하기
pivot_df = df.pivot(index='event_happened_week', columns='type', values='user_cnt')
pivot_df
그래프
pivot_df.plot(figsize=(20,6), marker = 'o')
plt.legend(title='event_type')
plt.xticks(rotation=45)
plt.show()
분석5 결론
- weekly_emails는 꾸준히 증가한다 → yammer측에서 유저에게 newsletter를 발신하는 것에는 문제가 없다.
- email_opens도 꾸준히 증가한다 → 유저들은 문제없이 수신한 이메일을 오픈했다.
- 7월말 ~ 8월초에 email_clickthrough가 감소했다 → 이메일 내부 링크를 통해 서비스에 접속한 유저가 감소했다. 이메일 내부 링크가 작동을 안할 수도 있다.
유저 인게이지먼트 하락 원인 분석의 최종 결론
2014-07말~ 2014-08초에 WAU가 감소한 이유는 아래와 같다.
- 비교적 최근 가입한 user보단 old users 사이에서 사용량이 감소했다.
- Device 중 mobile(phone, tablet) user의 사용량이 감소했다.
- Email 내의 링크 클릭량이 감소했다. 링크에 문제가 있을 수도 있다.
출처
💟 분석에 사용할 데이터와 분석할 문제에 대한 설명은 아래 링크에서 확인할 수 있다.
https://mode.com/sql-tutorial/a-drop-in-user-engagement
💟자세한 MySQL 코드
'SQL > 프로젝트' 카테고리의 다른 글
[데이터리안_플젝2] A/B test (1) | 2024.01.08 |
---|---|
[데이터리안_플젝1] 유저 인게이지먼트 하락 원인 분석 ① (0) | 2024.01.01 |