본문 바로가기
[코드쉐도잉]/산업

[DAU, Funnel] 어느 단계에서 이탈이 발생하는가?

by 에디터 윤슬 2025. 1. 14.

목표

- DAU(일간 활성자 수), MAU(월간 활성자 수) 추이는?
    - 어느 요일에 가장 많이 방문하는가?
- 사이트 체류시간 평균은?
    - 조회만 한 유저, 장바구니에 담은 유저, 구매까지 한 유저별 체류시간 비교
- 퍼널 분석
    - 어느 단계에서 가장 많은 이탈이 발생하는가?
    - 어느 집단(군집)에서 가장 많은 이탈이 발생하는가?

 

라이브러리 호출

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import warnings
warnings.filterwarnings("ignore")
from scipy import stats
import datetime as dt
pd.set_option('display.max_columns', None)

sns.set_style('whitegrid')
palette = 'Set2'

 

DAU, MAU

  • DAU 활성 사용자 수
# 활성 사용자 수(Active Users)는 특정 기간 동안 웹사이트나 애플리케이션에서 
# 특정 행동(로그인, 상품 조회, 구매, 게시물 작성 등)을 실제로 수행한 사용자 수를 의미합니다. 
# 이를 측정하기 위해 각 디바이스마다 UUID(Unique User ID)를 부여하여 특정 기간마다 
# 단 1회만 측정하여 사용자 수를 계산합니다. 

dau = merged_df.groupby('transaction_date')['customer_id'].nunique().reset_index().rename({'customer_id': 'dau'}, axis = 1)
mask = dau['transaction_date'] >= '2021-01-01'

fig = px.line(data_frame= dau[mask], x = 'transaction_date', y = 'dau')
fig.show()

# 위 그래프를 보니 주기성이 있어 보임. 요일 변수를 추가
# groupby로 요일별 평균 계산

dau['day_of_week'] = dau['transaction_date'].dt.day_name() # 요일 명칭
dau['numeric_of_day'] = dau['transaction_date'].dt.day_of_week # 요일 숫자로 표현. Monday: 0, sunday: 6
avg_dau = dau.groupby(['day_of_week', 'numeric_of_day'])['dau'].mean().reset_index()
avg_dau.sort_values('numeric_of_day', inplace=True)
avg_dau

  • 요일별 DAU 활성자 수 그래프
fig = px.bar(data_frame= avg_dau, x = 'day_of_week', y = 'dau', title = '요일별 DAU 평균', height= 600, width= 600)
fig.show()

 

  • MAU
data = merged_df.copy()
data['transaction_date'] = data['transaction_date'].dt.to_period('M')
data['transaction_date'] = data['transaction_date'].dt.to_timestamp()
mau = data.groupby('transaction_date')['customer_id'].nunique().reset_index().rename({'customer_id': 'mau'}, axis = 1)
mask = mau['transaction_date'] >= '2021-01'

fig = px.line(data_frame= mau[mask], x = 'transaction_date', y = 'mau', title = 'Monthly Active Users (MAU)')
fig.show()

  • 월 변수 추가
# 홀수 달마다 증가하는 추세가 보임. 월 변수를 추가

mau['month_name'] = mau['transaction_date'].dt.month_name()
mau['month'] = mau['transaction_date'].dt.month
mau

  • 시각화
mau['month_name'] = mau['transaction_date'].dt.month_name()
mau['month'] = mau['transaction_date'].dt.month
avg_mau = mau.groupby(['month_name', 'month'])['mau'].mean().reset_index()
avg_mau.sort_values('month', inplace=True)

fig = px.bar(data_frame= avg_mau, x = 'month_name', y = 'mau', title = '월별 MAU 평균', height= 600, width= 800)
fig.show()

 

체류 시간

# 세션별 체류 시간 구하기
click_stream_df['datetime'] = pd.to_datetime(click_stream_df['datetime'])
duration = click_stream_df.groupby(['session_id', 'event_date'])['datetime'].agg(['max', 'min']).reset_index()
duration['duration'] = duration['max'] - duration['min']
duration.head()

 

  • 평균 체류 시간
# 평균 체류시간
duration['duration'].mean()

>>> Timedelta('0 days 04:58:14.784757010')

 

  • 유저 세션별 퍼널 구분
# 유저 세션별 퍼널 구분

session_pivot = pd.pivot_table(data = click_stream_df, index='session_id', columns='event_name', values='datetime', aggfunc='count').reset_index().fillna(0)
session_pivot

 

  • 세션별 평균 체류 시간 계산
# 세션 ID 리스트 생성
add_cart_session = session_pivot.query('ADD_TO_CART > 0')['session_id'].tolist()
booking_session = session_pivot.query('BOOKING > 0')['session_id'].tolist()

# 평균 체류 시간 계산
view_session_avg_duration = duration.loc[
    ~duration['session_id'].isin(add_cart_session + booking_session), 'duration'
].mean()

cart_session_avg_duration = duration.loc[
    duration['session_id'].isin(add_cart_session), 'duration'
].mean()

purchase_session_avg_duration = duration.loc[
    duration['session_id'].isin(booking_session), 'duration'
].mean()

# 결과 출력
print(f'조회만 한 유저의 평균 체류시간, {view_session_avg_duration}')
print(f'카트에 담은 유저의 평균 체류시간, {cart_session_avg_duration}')
print(f'구매까지 한 유저의 평균 체류시간, {purchase_session_avg_duration}')

>>>
조회만 한 유저의 평균 체류시간, 0 days 00:32:03.295729386
카트에 담은 유저의 평균 체류시간, 0 days 04:59:39.909900820
구매까지 한 유저의 평균 체류시간, 0 days 05:00:51.770448794

 

Funnel Analysis

  • session별 pivot_table
funnel = session_pivot[['ADD_PROMO', 'ADD_TO_CART', 'BOOKING', 'CLICK',
       'HOMEPAGE', 'ITEM_DETAIL', 'PROMO_PAGE', 'SCROLL', 'SEARCH']].sum().to_frame().reset_index()
funnel.columns = ['event_type', 'count']
funnel.sort_values('count', ascending=False, inplace=True)
funnel.reset_index(inplace=True)
funnel.drop(columns='index', inplace=True)
funnel

fig = px.funnel(data_frame=funnel, x = 'event_type', y = 'count')
fig.update_traces(texttemplate = '%{value: ,.0f}') # 숫자 형식 지정
fig.show()

  • 퍼널별 전환율 계산
# 전환율 계산
data = funnel.copy()
data['retain_rate'] = data['count'] / data['count'].shift(1).fillna(data['count']) # 아래로 이동하는 함수 shift 사용

#시각화
fig = px.funnel(data_frame = data, x = 'event_type', y = 'retain_rate')
fig.update_traces(texttemplate = '%{value:,.2%}')
fig.show()

 

  • 클릭 - 장바구니 - 거래 전환율 계산
# 클릭 - 장바구니 - 거래 전환율 계산
data = funnel.iloc[[0, 2, 6]] # 클릭, 장바구니, 거래 선택
data['retain_rate'] = data['count'] / data['count'].shift(1).fillna(data['count'])

#시각화
fig = px.funnel(data_frame = data, x = 'event_type', y = 'retain_rate')
fig.update_traces(texttemplate = '%{value:,.2%}')
fig.show()