Post

파이썬 데이터분석 - A/B test 분석

A/B테스트 실습 : 멤버십 결제 페이지를 A안에서 B안으로 바꾸는게 좋을까?

1
2
3
4
import pandas as pd

data = pd.read_csv('/content/drive/MyDrive/30-day_Subscription_Data.csv')
data
Unnamed: 0DateTotal UsersSubscribers1-Month Subscribers12-Month SubscribersRevenueGroupConversion RateRevenue per User1-Month Revenue12-Month Revenue
002023-09-15459221931547A안0.0479301.067538665882
112023-09-16504262152205A안0.0515871.4087307351470
222023-09-17514262241946A안0.0505841.2062267701176
332023-09-18482231941841A안0.0477181.2240666651176
442023-09-19512251962429A안0.0488281.5429696651764
552023-09-20490242041876A안0.0489801.2244907001176
662023-09-21539252321393A안0.0463820.797774805588
772023-09-22450211831512A안0.0466671.066667630882
882023-09-23446221841806A안0.0493271.3004486301176
992023-09-24540252141911A안0.0462961.1296307351176
10102023-09-25512262062464A안0.0507811.5625007001764
11112023-09-26512262152205A안0.0507811.3867197351470
12122023-09-27593302462604A안0.0505901.4165268401764
13132023-09-28557262331687A안0.0466790.951526805882
14142023-09-29487231852100A안0.0472281.3963046301470
15152023-09-30513262241946A안0.0506821.2085777701176
16162023-10-01531252052170A안0.0470811.3182677001470
17172023-10-02523262152205A안0.0497131.3575537351470
18182023-10-03510231941841A안0.0450981.1568636651176
19192023-10-04502232031582A안0.0458170.996016700882
20202023-10-05431201731477A안0.0464041.090487595882
21212023-10-06528272431722A안0.0511361.022727840882
22222023-10-07461221841806A안0.0477221.2581346301176
23232023-10-08534242041876A안0.0449441.1235967001176
24242023-10-09464231941841A안0.0495691.2715526651176
25252023-10-10607312383157A안0.0510711.6968708052352
26262023-10-11460221931547A안0.0478261.065217665882
27272023-10-12476232121323A안0.0483190.861345735588
28282023-10-13606312472898A안0.0511551.5511558402058
29292023-10-14522272252240A안0.0517241.3793107701470
3002023-09-15516352873038B안0.0678291.8992259802058
3112023-09-16483332493486B안0.0683232.3602488402646
3222023-09-17488312472898B안0.0635251.9262308402058
3332023-09-18520393183437B안0.0750002.13461510852352
3442023-09-19498343042226B안0.0682731.40562210501176
3552023-09-20441322662674B안0.0725621.9501139101764
3662023-09-21429302642086B안0.0699301.5384629101176
3772023-09-22474302462604B안0.0632911.7721528401764
3882023-09-23577382993661B안0.0658582.06239210152646
3992023-09-24565362973073B안0.0637171.75221210152058
40102023-09-25433312562639B안0.0715941.9630488751764
41112023-09-26511362793591B안0.0704502.2896289452646
42122023-09-27532352962779B안0.0657891.67293210151764
43132023-09-28520373252590B안0.0711541.57692311201470
44142023-09-29459322662674B안0.0697171.8736389101764
45152023-09-30572393542401B안0.0681821.31118912251176
46162023-10-01524362973073B안0.0687021.88931310152058
47172023-10-02424292452310B안0.0683961.7452838401470
48182023-10-03519342773003B안0.0655111.8689799452058
49192023-10-04528383262884B안0.0719701.74242411201764
50202023-10-05525373521813B안0.0704761.0476191225588
51212023-10-06532362973073B안0.0676691.86090210152058
52222023-10-075043928114214B안0.0773812.7380959803234
53232023-10-085543828103920B안0.0685922.3104699802940
54242023-10-09510352783297B안0.0686272.0980399452352
55252023-10-10509342862744B안0.0667981.7288809801764
56262023-10-11543352783297B안0.0644571.9705349452352
57272023-10-12520342862744B안0.0653851.6923089801764
58282023-10-13475352783297B안0.0736842.2526329452352
59292023-10-14497292272828B안0.0583501.8511077702058
1
data.info()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 60 entries, 0 to 59
Data columns (total 12 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   Unnamed: 0            60 non-null     int64  
 1   Date                  60 non-null     object 
 2   Total Users           60 non-null     int64  
 3   Subscribers           60 non-null     int64  
 4   1-Month Subscribers   60 non-null     int64  
 5   12-Month Subscribers  60 non-null     int64  
 6   Revenue               60 non-null     int64  
 7   Group                 60 non-null     object 
 8   Conversion Rate       60 non-null     float64
 9   Revenue per User      60 non-null     float64
 10  1-Month Revenue       60 non-null     int64  
 11  12-Month Revenue      60 non-null     int64  
dtypes: float64(2), int64(8), object(2)
memory usage: 5.8+ KB
1
data.isna().sum()
0
Unnamed: 00
Date0
Total Users0
Subscribers0
1-Month Subscribers0
12-Month Subscribers0
Revenue0
Group0
Conversion Rate0
Revenue per User0
1-Month Revenue0
12-Month Revenue0


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from scipy.stats import ttest_ind

# `Date` 열을 datetime 형식으로 변환
data['Date'] = pd.to_datetime(data['Date'])

# 불필요한 `Unnamed: 0` 열 삭제
data.drop(columns=['Unnamed: 0'], inplace=True)

# 그룹별 전환율, 구독자 비율, 수익의 평균과 표준 편차 계산
group_stats = data.groupby('Group').agg(
    mean_conversion_rate=('Conversion Rate', 'mean'),
    std_conversion_rate=('Conversion Rate', 'std'),
    mean_subscribers=('Subscribers', 'mean'),
    std_subscribers=('Subscribers', 'std'),
    mean_revenue=('Revenue', 'mean'),
    std_revenue=('Revenue', 'std')
)

# 그룹별 1개월, 12개월 구독자 비율의 평균과 표준 편차를 계산
group_stats['mean_1month_ratio'] = data.groupby('Group')['1-Month Subscribers'].sum() / data.groupby('Group')['Subscribers'].sum()
group_stats['mean_12month_ratio'] = data.groupby('Group')['12-Month Subscribers'].sum() / data.groupby('Group')['Subscribers'].sum()

# t-검정을 사용하여 그룹 간 전환율, 구독자 비율, 수익, 1개월 구독자 비율, 12개월 구독자 비율의 차이를 검증
a_data = data[data['Group'] == 'A안']
b_data = data[data['Group'] == 'B안']
t_test_results = pd.DataFrame(
    {
        't_stat': [
            ttest_ind(a_data['Conversion Rate'], b_data['Conversion Rate'])[0],
            ttest_ind(a_data['Subscribers'], b_data['Subscribers'])[0],
            ttest_ind(a_data['Revenue'], b_data['Revenue'])[0],
            ttest_ind(a_data['1-Month Subscribers'] / a_data['Subscribers'], b_data['1-Month Subscribers'] / b_data['Subscribers'])[0],
            ttest_ind(a_data['12-Month Subscribers'] / a_data['Subscribers'], b_data['12-Month Subscribers'] / b_data['Subscribers'])[0],
        ],
        'p_value': [
            ttest_ind(a_data['Conversion Rate'], b_data['Conversion Rate'])[1],
            ttest_ind(a_data['Subscribers'], b_data['Subscribers'])[1],
            ttest_ind(a_data['Revenue'], b_data['Revenue'])[1],
            ttest_ind(a_data['1-Month Subscribers'] / a_data['Subscribers'], b_data['1-Month Subscribers'] / b_data['Subscribers'])[1],
            ttest_ind(a_data['12-Month Subscribers'] / a_data['Subscribers'], b_data['12-Month Subscribers'] / b_data['Subscribers'])[1],
        ]
    },
    index=['전환율', '구독자 수', '수익', '1개월 구독자 비율', '12개월 구독자 비율']
)

# 결과 출력
print("\n그룹별 전환율, 구독자 비율, 수익의 평균과 표준 편차:\n", group_stats)
print("\nt-검정 결과:\n", t_test_results)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
그룹별 전환율, 구독자 비율, 수익의 평균과 표준 편차:
        mean_conversion_rate  std_conversion_rate  mean_subscribers  \
Group                                                                
A안                 0.048621             0.002077         24.766667   
B안                 0.068373             0.003874         34.566667   

       std_subscribers  mean_revenue  std_revenue  mean_1month_ratio  \
Group                                                                  
A안            2.712466   1971.900000   431.619193           0.827725   
B안            3.002107   2945.133333   535.137993           0.806172   

       mean_12month_ratio  
Group                      
A안               0.172275  
B안               0.193828  

t-검정 결과:
                 t_stat       p_value
전환율         -24.610706  2.187760e-32
구독자 수       -13.266637  3.234488e-19
수익           -7.753532  1.587098e-10
1개월 구독자 비율    1.950202  5.599070e-02
12개월 구독자 비율  -1.950202  5.599070e-02
  • 그룹별 전환율과 구독자 수, 수익은 p-값이 0.05 미만으로 통계적으로 유의미한 차이를 보임
  • 1개월 구독자 비율과 12개월 구독자 비율은 p-값이 0.05보다 크게 나타나 통계적으로 유의미한 차이를 보이지 않음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
ax1 = sns.lineplot(data=data, x='Date', y='Conversion Rate', hue='Group')
plt.title('시간에 따른 전환율 변화')
plt.xlabel('날짜')
plt.ylabel('전환율')
plt.legend(title='그룹')
plt.xticks(rotation=45)

for line in ax1.lines:
    y_data = line.get_ydata()
    x_data = line.get_xdata()
    for x, y in zip(x_data, y_data):
        ax1.text(x, y, f'{y:.2f}', fontsize=9, ha='center', va='bottom')

plt.tight_layout()
plt.show()

png

  • 전반적으로 전환율은 A안보다 B안이 높게 형성됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
plt.figure(figsize=(10, 6))
ax2 = sns.lineplot(data=data, x='Date', y='Subscribers', hue='Group')
plt.title('시간에 따른 구독자 수 변화')
plt.xlabel('날짜')
plt.ylabel('구독자 수')
plt.legend(title='그룹')
plt.xticks(rotation=45)

for line in ax2.lines:
    y_data = line.get_ydata()
    x_data = line.get_xdata()
    for x, y in zip(x_data, y_data):
        ax2.text(x, y, f'{y:.0f}', fontsize=9, ha='center', va='bottom')

plt.tight_layout()
plt.show()

png

  • A안보다 B안의 구독자 수가 더 높게 나타남
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
plt.figure(figsize=(10, 6))
ax3 = sns.lineplot(data=data, x='Date', y='Revenue', hue='Group')
plt.title('시간에 따른 수익 변화')
plt.xlabel('날짜')
plt.ylabel('수익')
plt.legend(title='그룹')
plt.xticks(rotation=45)

for line in ax3.lines:
    y_data = line.get_ydata()
    x_data = line.get_xdata()
    for x, y in zip(x_data, y_data):
        ax3.text(x, y, f'{y:.2f}', fontsize=9, ha='center', va='bottom')

plt.tight_layout()
plt.show()

png

  • 수익또한 A안보다 B안이 더 높게 형성됨

  • 결론 : B안(변경안)을 채택하는 것을 제안함
  • 근거
    • B안은 A안보다 전환율과 수익이 통계적으로 유의미하게 증가함
    • 통계적으로 유의미하지는 않지만 12개월 구독자 비율도 B안이 더 증가하는 경향이 있음
    • 현금 흐름 개선을 위해 12개월 구독자 비율이 높은 B안 채택이 합리적인 선택요소 중 하나임
This post is licensed under CC BY 4.0 by the author.