본문 바로가기

TIL

[240213] 유저ID와 방문시작시간으로 세션ID/평균세션시간 생성(feat. ECDF)

* 자료 출처: Kaggle - Google Analytics Customer Revenue Prediction

 

1. 문제 배경

- GA 로그데이터로 유저별 평균 세션 시간을 ECDF로 시각화 

- 평균 세션 시간 데이터가 따로 제공되지 않아 User_id와 방문시작시간 칼럼을 활용해 세션ID 생성 

 

2. 세션ID 생성

- 각 유저의 방문시작시간의 차가 30분 이내인 값에 동일한 세션ID 부여

- 방문시작시간의 차가 30분 이상일 경우 다른 세션ID 부여 

- 1) 방문시작시간으로 정렬하고, 2) diff로 전 행과의 시간 차이가 30분 미만인지 체크하고, 3) cumsum으로 id 부여

## sessoin_id 생성하기 
# 1. 시간, 방문자id 기준으로 정렬한 데이터 프레임 생성
df_sorted = df[['visitStartTime', 'fullVisitorId']]
df_sorted = df_sorted.sort_values(by=['visitStartTime', 'fullVisitorId'])
df_sorted['visit_ymd'] = df_sorted['visitStartTime'].dt.strftime('%Y-%m-%d')

# 2.  유저id와 일별로 세션 시작시간 차이 diff()하여, ['diff']열 생성 
df_sorted['diff'] = df_sorted.groupby(['fullVisitorId', 'visit_ymd'])['visitStartTime'].diff()

# 3. 조건에 따라 cumsum하여 id 부여
# sessoin_id 부여할 조건절 변수 지정 # diff 값 null이거나 30분 이상  
diff_cond = (df_sorted['diff'].isnull()) | (df_sorted['diff'] > dt.timedelta(minutes=30))
# 조건이 true면, cumsum을, 아니면 null을 출력
df_sorted['new_id'] = np.where(diff_cond, diff_cond.cumsum(), None)
# 데이터프레임을 id 기준으로 재정렬 
df_sorted = df_sorted.sort_values(by='fullVisitorId')    
# diff_cond 조건에 맞지 않아 null 처리된 열은 위 행과 동일한 id 값 부여 
df_sorted['new_id'] = df_sorted['new_id'].fillna(method='ffill')

- 세션id 적용 예시

 

- 그룹바이하여 diff와 cumsum을 한 번에 처리해보고자 했으나 세션ID가 제대로 분류되지 않아 상기 방법 활용 

# 잘못된 코드
# 유저별로 그룹핑하여 방문시간 차이가 30분 이상인 경우 신규 값 지정  
# 동일 일자 방문 시간 30분 이내에 3번 이상 방문한 경우 모두 동일한 세션id가 부여되지 않음 
# 2개는 동일한 세션ID, 1개는 다른 세션ID가 부여됨
df_sorted['session_cum_id'] = (df_sorted.groupby(['fullVisitorId','visit_ymd'])['visitStartTime'].diff(axis=0, periods=1) >= dt.timedelta(minutes=30)).transform('cumsum')

 

 

3. 평균 세션 시간 구하기

- 평균 세션 시간은 유저별 일별 세션시간의 평균 값을 구해야 함

└ (마지막 세션시간-최초 세션시간 / 총 세션수 )

## 평균 세션 시간 구하기 
# 유저별-일별로 max, mim time & total session count 생성
df_sorted['daily_max_time'] = df_sorted.groupby(['fullVisitorId','visit_ymd'])['visitStartTime'].transform('max')
df_sorted['daily_min_time'] = df_sorted.groupby(['fullVisitorId','visit_ymd'])['visitStartTime'].transform('min')
df_sorted['daily_total_seesion'] = df_sorted.groupby(['fullVisitorId','visit_ymd'])['new_id'].transform('nunique')

# 유저별 일별 세션 시간
# max time - min time / total session 으로 일 평균 세션 시간 구하기
df_sorted['daily_session_time'] = (df_sorted['daily_max_time']-df_sorted['daily_min_time']) 
df_sorted['daily_avg_session_time'] = (df_sorted['daily_max_time']-df_sorted['daily_min_time']) / df_sorted['daily_total_seesion']
# 초 > 분으로 단위 변경
df_sorted['daily_avg_session_time'] = df_sorted['daily_avg_session_time'].dt.seconds / 60

# 유저별 평균 세션 시간
# 평균 세션 시간이 0인 비중이 94%
# 0포함 버전 
df_avg_session_time = pd.DataFrame(df_sorted.groupby('fullVisitorId')['daily_avg_session_time'].mean())
# 0제외 버전 
df_session_time_not_zero = df_sorted[df_sorted['daily_avg_session_time'] > 0]
df_avg_session_time_not_zero = pd.DataFrame(df_session_time_not_zero.groupby('fullVisitorId')['daily_avg_session_time'].mean())

 

4. ECDF 시각화 

- ECDF는 Empirical cumulative distribution function의 약어로 경험적 누적 분포 함수라고 부름

- 서로 다른 표본의 분포를 비교할 때 주로 사용하며, 각 집단의 백분위를 추정

#ECDF 그래프 시각화
plt.figure(figsize= (12,5))
plt.subplot(1,2,1)
sns.ecdfplot(data=df_avg_session_time, x='daily_avg_session_time')
plt.subplot(1,2,2)
sns.ecdfplot(data=df_avg_session_time_not_zero, x='daily_avg_session_time')

좌측은 0을 포함한 버전, 우측은 0을 제외한 버전