Notice
Recent Posts
Recent Comments
Link
«   2026/02   »
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
Tags
more
Archives
Today
Total
관리 메뉴

Silver bullet

그로스 해킹을 위한 파이썬 통계 분석 본문

AI/AI

그로스 해킹을 위한 파이썬 통계 분석

밀크쌀과자 2024. 7. 4. 16:30

1. Correlation Analysis for Marketing costs & User acquisition (with 피어슨 상관 계수)

  • 매월 유튜브에 광고 비용을 지출하여 신규 유저(구매 고객 or 회원가입 고객)를 획득
  • 아래의 ad_df DataFrame 과 같이 월별로 10,000원 단위의 유튜브 광고 비용과 해당 월에 신규로 획득된 유저 수가 측정되었다고 가정
import numpy as np
import pandas as pd
from scipy import stats

marketing_costs = [352, 164, 210, 425, 503, 232, 321, 556, 464, 578, 612, 434] # 집행된 유튜브 광고 비용 (단위 : 10,000원)
user_acquired = [7214, 6122, 6896, 8020, 10982, 9021, 9240, 10210, 9987, 11521, 9792, 9852] # (단위 : 명)

ad_df = pd.DataFrame({'Marketing_Costs':marketing_costs, 
                      'User_Acquired':user_acquired})
ad_df

단순 CAC(Customer Acquisition Cost, 신규고객 유치 비용) 계산 (참고 : https://j.mp/35O5NRe)

cac = ad_df['Marketing_Costs'].sum() / ad_df['User_Acquired'].sum() # 총 마케팅 비용 / 총 신규고객 수

print('1명의 신규 유저를 획득하기 위한 평균적인 유튜브 광고 비용 : {:.0f}원'.format(cac * 10000)) # .0f : 소수점 0번째 자리까지
# 1명의 신규 유저를 획득하기 위한 평균적인 유튜브 광고 비용 : 446원

위의 금액에 추가로 획득하기를 원하는 유저 수를 곱한 금액을 유튜브 광고 비용으로 쓰면 그만큼 유저가 늘어날까?

== 위의 금액 만큼 유튜브 광고에 쓰면 정말로 유저가 1명 늘어날까?

ad_df.corr() # DataFrame의 "Corr"elation 함수로는 p-value값까지 파악할 수 없음
# Pearson correlation coefficient and p-value for testing non-correlation

result = stats.pearsonr(ad_df['Marketing_Costs'], ad_df['User_Acquired']) # Pearson’s co"r"relation coefficient

print("피어슨 상관계수 :", result[0])
print('p-value :', result[1])

# 피어슨 상관계수 : 0.803577506954685
# p-value : 0.0016386012345537423

위 결과를 보았을 때 p-value가 0.0016(<0.05)이므로,
월별 유튜브 광고 비용과 신규 유저 수가 통계적으로 유의미한 상관관계가 없다(상관계수==0)는 전제 하에 이러한 측정 결과가 나올 확률이 0.16%라고 이해할 수 있음

-> 월별 유튜브 광고 비용과 신규 유저 수 사이에는 통계적으로 유의미한 강한 상관관계(+0.8)가 있다.


+ 피어슨 상관계수 값에 대한 해석 기준 (Strong/Moderate/Weak) : https://j.mp/3mH8FWN

+ 파이썬 프로그래밍 없이 상관관계 분석을 진행할 수 있는 도구 : https://j.mp/324551c

 

Pearson Correlation Coefficient Calculator

Pearson's correlation coefficient measures the strength and direction of the relationship between two variables. To begin, you need to add your data to the text boxes below (either one value per line or as a comma delimited list). So, for example, if you w

www.socscistatistics.com

 

2. A/B Test for Duration Time (with 독립표본 t-test)

  • 페이지 구성과 세부 디자인을 다르게 만든 2개의 웹사이트 시안을 기반으로 A/B Test 진행
  • 웹사이트 시안 A와 B 각각에 유입된 유저들이 실제로 각 웹사이트 내에서 이탈하기까지의 시간(체류시간, Duration time)을 측정
  • 아래의 web_a & web_b DataFrame 과 같이 각 시안별로 유저들의 체류 시간이 측정되었다고 가정
web_a = pd.DataFrame([20.5, 12.6, 19.5, 18.8, 13.4, 13.5, 17.5, np.nan, 12.8, 17.8, np.nan, 23.1, 10.6, np.nan, 11.5], 
                     columns=['Duration_A'])
web_b = pd.DataFrame([11.8, 10.7, np.nan, 12.5, np.nan, 14.9, 12.1, 13.9, 10.3, 9.0, 13.3, 12.4, 12.5], 
                     columns=['Duration_B'])
                     
df_concat = pd.concat([web_a, web_b], axis=1) # Concatenate
df_concat
print(web_a.mean()) # 웹사이트 시안 A에 대한 유저들의 "평균" 체류시간
print(web_b.mean()) # 웹사이트 시안 B에 대한 유저들의 "평균" 체류시간

# Duration_A    15.966667
# Duration_B    12.127273

위의 웹사이트 시안 A/B 간 평균 체류시간의 차이가 우연의 일치였을까?

# t-test를 진행하기 이전에 null(np.nan, N/A)인 데이터들은 제외시켜줘야 합니다.

web_a = web_a.dropna() # DataFrame에서 N/A 데이터(missing data)가 포함된 행들을 떨궈(drop)낸다.
web_b = web_b.dropna()

print(web_a.var(), web_b.var())
# Duration_A    16.404242
# Duration_B    2.782182
# 독립표본 t-test (두 집단간의 평균차이를 검정)

# T-test for "ind"ependent samples
stats.ttest_ind(web_a["Duration_A"], # 해당 열의 '값들'
                web_b["Duration_B"], # 해당 열의 '값들'
                equal_var=False)

# 두 집단의 분산값이 다르거나, 샘플 수가 같지 않다면 equal_var=False
# equal_var=True : perform a standard independent 2 sample test that assumes equal population variances.
# equal_var=False : perform Welch's t-test, which does not assume equal population variance.
# -> 위 2개의 체류시간 측정 그룹(샘플)이 동일한 수(샘플 사이즈)가 아니거나 유사한 분산값을 갖지 않을 경우 Welch's t-test를 사용 (https://j.mp/3kLFwcE)
# 두 샘플 그룹 간 분산값 차이 확인 : print(web_a.var(), web_b.var())

# Ttest_indResult(statistic=3.0165632092150694, pvalue=0.008734970056646718)

위 결과를 보았을 때 p-value가 0.0087(<0.05)이므로,
웹 시안 A와 B에 대한 체류시간의 평균값이 통계적으로 유의미한 차이가 없다는 전제 하에 이러한 체류시간 측정 결과가 나올 확률이 0.87%라고 이해할 수 있음

-> 웹 시안 A와 B에 대한 유저들의 체류 시간 사이에는 통계적으로 유의미한 차이가 있다 (A가 평균적으로 더 오래 체류하게끔 함)


+ 파이썬 프로그래밍 없이 t-test를 진행할 수 있는 각종 도구 : https://j.mp/34O9yaa / https://j.mp/381BVni

 

3. A/B Test for Click-Through Rate or Conversion Rate (with 카이제곱 검정)

  • 최종 구매를 위한 버튼(혹은 광고 배너)을 2개의 서로 다른 시안으로 제작해 각기 다른 유저들에게 노출
  • 해당 버튼을 누르면 구매가 확정(랜딩 페이지로 연결)된다고 가정
  • Conversion rate (전환율) : a metric, shown as a percentage, that displays how many website or app visitors complete an action out of the total number of visitors.
  • Click-Through Rate (CTR, 클릭율) : a metric, shown as a percentage, that measures how many people clicked your ad to visit a website or landing page.
  • 시안 A/B별로 아래 click_df DataFrame과 같이 클릭 수 & (유저에게 노출되었으나) 미클릭 수가 측정되었다고 가정
a_clicked = 144 # 버튼(배너) 시안 A를 누른 유저의 수
a_unclicked = 2362 # 버튼(배너) 시안 A를 보았으나 누르지 않은 유저의 수

b_clicked = 212 # 버튼(배너) 시안 B를 누른 유저의 수
b_unclicked = 2528 # 버튼(배너) 시안 B를 보았으나 누르지 않은 유저의 수

click_df = pd.DataFrame({'Clicked':[a_clicked, b_clicked], 
                         'Unclicked':[a_unclicked, b_unclicked]}, index=['Button_A', 'Button_B'])
click_df
# 단순한 전환율 및 클릭율 계산 
# : 실제 클릭 수 / 전체 노출 수 * 100

conversion_rate = click_df['Clicked'] / (click_df['Clicked'] + click_df['Unclicked']) * 100
# print(conversion_rate)

print("Button(or Banner Ad) A's Conversion Rate(or CTR) is : {:0.2f}%".format(conversion_rate[0]))
print("Button(or Banner Ad) B's Conversion Rate(or CTR) is : {:0.2f}%".format(conversion_rate[1]))
# Button(or Banner Ad) A's Conversion Rate(or CTR) is : 5.75%
# Button(or Banner Ad) B's Conversion Rate(or CTR) is : 7.74%

위의 버튼(배너) A/B 간 전환율(클릭율) 차이가 우연의 일치였을까?

- 위와 같이 데이터가 정리된 표를 Contingency table(분할표)이라고 부릅니다. (참고 : https://j.mp/384CcFR)

- chi2_contingency 함수를 활용하면 위와 같은 Contingency table을 기반으로 카이제곱 검정을 할 수 있습니다. (참고 : https://j.mp/3mH1Nsr)

 

  • Button_A -> 남자, Button_B -> 여자, Clicked -> 제품 A 구매자 수, Unclicked -> 제품 A 미구매자 수일 경우,
  • 위 Contingency table는 남녀 각각에 대한 제품 A의 구매자 수와 미구매자 수에 대한 데이터가 됩니다.
  • 이 때 chi2_contingency 함수에는 구매자 수 열 미구매자 수 열을 차례대로 넘겨줘야 합니다. (제품 A 구매 여부와 성별에 대한 연관성 분석)
# "Chi-square" test of independence of variables in a "contingency" table.

stats.chi2_contingency([click_df['Clicked'], click_df['Unclicked']])[1] # 2번째 return 값이 p-value에 해당

# 0.004968535119697213

위 결과를 보았을 때 p-value가 0.0049(<0.05)이므로,
버튼 A/B(범주형 변수 1)에 대한 클릭 여부(범주형 변수 2)가 통계적으로 유의미한 연관성이 없다(서로 독립적이다)는 전제 하에,
이러한 클릭 수 측정 결과가 나올 확률이 0.49%라고 이해할 수 있음

-> 버튼(배너) 시안 A와 B에 대한 유저들의 클릭 수 사이에는 통계적으로 유의미한 차이가 있다 (버튼(배너) B가 더 많은 클릭을 유도함)


+ 파이썬 프로그래밍 없이 분할표를 기반으로 한 카이제곱 검정을 진행할 수 있는 도구 : https://j.mp/3efVGbx / Optimizely (UI 기반 A/B test tool) @ https://goo.gl/uh9P9A

 

 

+ 추가 학습 자료

 

- A/B Testing에 대한 기초적인 정보들 (정의/효과/사례/샘플크기산정 등) @ https://j.mp/3eeo2TA

- 데이터를 활용한 디지털 마케팅 효과분석 (부스트코스, A/B테스트 / GA / 엑셀통계분석) @ https://j.mp/2P7YHCO

- P-hacking에 대하여 @ https://j.mp/3mLMOgP / https://j.mp/31XJBTF

 

- [통계학 기초] 통알못을 위한 통계튜브 (통통튜브) @ https://bit.ly/36xdcsJ

- [통계학 기초] Statistics Fundamentals (StatQuest) @ https://bit.ly/3iKzyt0

- [통계학 기초] Introduction to Basic Statistics in Python (한글, 확률/분포/기대값/점추정/구간추정/가설검정 등) @ https://bit.ly/3O87Y7e

 


  • p-value의 높고 낮음과 별개로 실제 실험의 효과 크기 역시도 중요하게 고려해야한다.
    예를 들어 어떤 웹사이트의 구매 버튼의 디자인을 변경하여 구매 수가 n 만큼 증가되었고,
    디자인 변경 전/후에 대한 구매 버튼 클릭 수 사이의 관계를 대상으로 통계 검정 후 p-value가 0.05보다 낮게 나왔더라도,
    정작 증가된 구매 수에 해당하는 n이 미미하다면 낮은 p-value에도 불구하고 디자인 변경의 실질적인 효용이 적기 때문이다.
    (통계적으로만 유의미할 뿐 독립변수의 변화에 따른 종속변수의 변화값이 실질적/실용적인 의미를 갖지 않음)