데이터분석 6기/본캠프

2025-05-13 통계 검증 및 인사이트 정리

seyeon1130 2025. 5. 13. 21:01

문제 정의 : 재방문율이 너무 낮다

저번에는 시간 순서로 다음 강의를 듣는지 안듣는지였다면 이번에는

고객이 한 강의만 들었는지 여러 강의를 들었는지 보는 multiple 컬럼을 만들었다.

df['multiple'] = df['userid_DI'].duplicated(keep=False)

d = df.groupby('userid_DI').agg(
    multiple=('multiple', 'first'),
    age_group=('age_group', 'first')
).reset_index()


sns.countplot(x='multiple',data=d)
plt.title('한 강의만 본 학생 vs 여러 강의를 본 학생')

for p in plt.gca().patches:
    plt.text(p.get_x() + p.get_width() / 2, p.get_height(),
             f'{int(p.get_height())}명', ha='center', va='bottom', fontsize=12)
plt.show()

전체 402843명의 학생 중 한 강의만 수강한 학생이 318701명

다들 한 강의만 수강했기 때문에 재방문을 높일 필요가 있어 보인다.

 

가설 1: 연령대 별로 재방문율이 다를 것이다.

# 나이대별 다음 강의 수강 비율 계산
age_course = d.groupby('age_group')['multiple'].mean().reset_index()
age_course['multiple'] *= 100

# 시각화
plt.figure(figsize=(12, 6))
bars = sns.barplot(x='age_group', y='multiple', data=age_course)

sorted_indices = age_course['multiple'].nlargest(2).index
for i, bar in enumerate(bars.patches):
    if i in sorted_indices:
        bar.set_facecolor('deepskyblue')
    else:
        bar.set_facecolor('gray')

for p in plt.gca().patches:
    plt.gca().annotate(f'{p.get_height():.2f}%', 
                       (p.get_x() + p.get_width() / 2., p.get_height()), 
                       ha='center', va='baseline', fontsize=12)

plt.title('연령대별 다음 강의 수강 비율"')
plt.show()

import numpy as np
from scipy import stats

a0 = df[df['multiple'] == 0]
a0 = a0['age']

a1 = df[df['multiple'] == 1]
a1 = a1['age']

t, pvalue = stats.ttest_ind(c0, c1) 
print('나이와 다음 강의 수강 여부 ttest')
print(f"t값: {t}")
print(f"p값: {pvalue}")
나이와 다음 강의 수강 여부 ttest
t값: -678.0349267674329
p값: 0.0

t검정 결과 p값이 0.05 이하이므로 통계적으로 유의미하다.

10대~20대 타겟 맞춤 추천 시스템:

  • 수강 중인 강의의 카테고리와 동일한 강의 추천.
  • 최근 30일 내 10대~20대 사용자에게 인기 있는 강의 우선 추천.

가설 2: 수료한 사람이 재방문율이 높을 것이다

# 상태별 다음 강의 수강 비율 계산
status_next_course = df.groupby('status')['next_course'].mean().reset_index()
status_next_course['next_course'] *= 100

# 시각화
plt.figure(figsize=(12, 6))
bars = sns.barplot(x='status', y='next_course', data=status_next_course)

sorted_indices = status_next_course['next_course'].nlargest(1).index
for i, bar in enumerate(bars.patches):
    if i in sorted_indices:
        bar.set_facecolor('deepskyblue')
    else:
        bar.set_facecolor('gray')


for p in plt.gca().patches:
    plt.gca().annotate(f'{p.get_height():.2f}%', 
                       (p.get_x() + p.get_width() / 2., p.get_height()), 
                       ha='center', va='baseline', fontsize=12)

plt.title('상태별 다음 강의 수강 여부 비율')
plt.show()

from scipy.stats import chi2_contingency

result = pd.crosstab(df['status'], df['next_course'])

chi2, p, dof, expected = chi2_contingency(result)
print('상태와 다음 강의 수강 여부')
print(f"카이제곱 통계량: {chi2:.3f}, p-value값: {p:.3f}, 자유도: {dof}")
상태와 다음 강의 수강 여부
카이제곱 통계량: 885.251, p-value값: 0.000, 자유도: 2

 

카이제곱 검정 결과 수료 상태와 다음 강의 수강 여부가 독립적이지 않다. 

p값이 0.05 이하이므로 귀무가설 기각.

 

수료자 맞춤 추천 시스템:

  • 학습 완료 시 "학습 마스터" 배지 지급.
  • 수료 후 심화 강의 자동 추천 → "더 깊이 배우고 싶다면, 다음 강의로!"

수료율을 높이는 방법: 장기 학습

# 수료 비율 카테고리별 시각화
certification_rate = df.groupby('activity_category')['certified'].mean().reset_index()
certification_rate['certified'] *= 100
plt.figure(figsize=(12, 6))
bars = sns.barplot(x='activity_category', y='certified', data=certification_rate)

sorted_indices = certification_rate['certified'].nlargest(2).index
for i, bar in enumerate(bars.patches):
    if i in sorted_indices:
        bar.set_facecolor('deepskyblue')
    else:
        bar.set_facecolor('gray')

for p in plt.gca().patches:
    plt.gca().annotate(f'{p.get_height():.2f}%', 
                       (p.get_x() + p.get_width() / 2., p.get_height()), 
                       ha='center', va='baseline', fontsize=12)
plt.title('카테고리별 수료율 (%)')
plt.show()

import numpy as np
from scipy import stats

c0 = df[df['certified'] == 0]
c0 = c0['ndays_act']

c1 = df[df['certified'] == 1]
c1 = c1['ndays_act']

t, pvalue = stats.ttest_ind(c0, c1, equal_var=False) 
print('학습일과 수료 ttest')
print(f"t값: {t}")
print(f"p값: {pvalue}")
학습일과 수료 ttest
t값: -191.4522830062952
p값: 0.0

t테스트 결과 수료 여부에 따른 학습일은 통계적으로 유의미한 차이가 있다.

 

# 액티비티 카테고리별 다음 강의 수강 비율 계산
activity_next_course = df.groupby('activity_category')['next_course'].mean().reset_index()
activity_next_course['next_course'] *= 100

# 시각화
plt.figure(figsize=(12, 6))
bars = sns.barplot(x='activity_category', y='next_course', data=activity_next_course)

sorted_indices = activity_next_course['next_course'].nlargest(2).index
for i, bar in enumerate(bars.patches):
    if i in sorted_indices:
        bar.set_facecolor('deepskyblue')
    else:
        bar.set_facecolor('gray')


for p in plt.gca().patches:
    plt.gca().annotate(f'{p.get_height():.2f}%', 
                       (p.get_x() + p.get_width() / 2., p.get_height()), 
                       ha='center', va='baseline', fontsize=12)

plt.title('액티비티 카테고리별 다음 강의 수강 비율 (시간적 순서 기준)')
plt.show()

n0 = df[df['next_course'] == 0]
n0 = n0['ndays_act']

n1 = df[df['next_course'] == 1]
n1 = n1['ndays_act']

t, pvalue = stats.ttest_ind(n0, n1, equal_var=False) 
print('학습일과 재방문 ttest')
print(f"t값: {t}")
print(f"p값: {pvalue}")
학습일과 재방문 ttest
t값: -32.70660823122103
p값: 7.737018523246995e-234

재방문 여부에 따른 학습일 또한 유의미한 차이가 있다.

 

장기 학습자 맞춤 추천 시스템:

강의 구조 개선:

  • 각 강의는 최소 30일 이상 학습할 수 있도록 단계별 콘텐츠 구성.
  • 예: 1주차 기초 → 2주차 중급 → 3주차 심화 → 4주차 마무리 퀴즈.

출석 체크 및 연속 학습 보상:

  • 매일 로그인 시 출석 체크 → 누적 일수에 따라 리워드 제공.
  • 30일 연속 학습 시 수료증 및 "학습 마스터" 배지 제공.
  • 장기 학습자 전용 보상: 30일 이상 학습한 사용자에게 할인 쿠폰 제공.

 

30일 이상 학습하게 하는 방법: 강의 콘텐츠 탐색

# 그래프 그리기
fig, axes = plt.subplots(1, 2, figsize=(18, 6))

# 콘텐츠를 본 비율
view_rate = df.groupby('activity_category')['viewed'].mean().reset_index()
view_rate['viewed'] *= 100
sns.barplot(x='activity_category', y='viewed', data=view_rate, ax=axes[0])
axes[0].set_title('강의 콘텐츠를 본 비율 (%)')
for p in axes[0].patches:
    axes[0].annotate(f'{p.get_height():.2f}%', 
                     (p.get_x() + p.get_width() / 2., p.get_height()), 
                     ha='center', va='baseline', fontsize=12)
    
# 콘텐츠 적극적으로 본 비율
explored_rate = df.groupby('activity_category')['explored'].mean().reset_index()
explored_rate['explored'] *= 100
sns.barplot(x='activity_category', y='explored', data=explored_rate, ax=axes[1])
axes[1].set_title('강의 콘텐츠를 적극적으로 본 비율 (%)')
for p in axes[1].patches:
    axes[1].annotate(f'{p.get_height():.2f}%', 
                     (p.get_x() + p.get_width() / 2., p.get_height()), 
                     ha='center', va='baseline', fontsize=12)

plt.tight_layout()
plt.show()

viewed는 학습일과 관계없이 일정한 비율을 유지. explored는 학습일이 증가할수록 비율이 크게 상승.

즉 강의 콘텐츠를 그냥 보는 것이 아닌 탐색하는 것이 장기 학습에 도움이 된다는 것을 파악함.

e0 = df[df['explored'] == 0]
e0 = e0['ndays_act']

e1 = df[df['next_course'] == 1]
e1 = e1['ndays_act']

t, pvalue = stats.ttest_ind(e0, e1, equal_var=False) 
print('학습일과 콘텐츠 탐색 여부 ttest')
print(f"t값: {t}")
print(f"p값: {pvalue}")
학습일과 콘텐츠 탐색 여부 ttest
t값: -73.83936486017481
p값: 0.0

콘텐츠 탐색 여부에 따른 학습일도 유의미한 차이 있음,

 

단기 학습자 맞춤 학습 시스템

  • "학습 자료 더 보기" 버튼 상시 노출.
  • 예: "추가 예제 보기", "심화 설명 확인하기".
  • 3일 이상 탐색하지 않으면 "추가 콘텐츠 탐색해보세요" 알림 발송.