02. A/B 테스팅 (Experiments)
1. 개요 및 시나리오
상황: 마케팅 팀이 새로운 랜딩 페이지(B안)를 만들었습니다. “기존 페이지(A안)보다 전환율이 2% 올랐어요!”라고 기뻐합니다.
하지만 당신은 침착하게 묻습니다.
“표본 수는 몇 명이었나요? 그 2% 상승이 우연일 확률(P-value)은 얼마인가요?”
A/B 테스트는 비즈니스 의사결정의 꽃입니다. 이번 챕터에서는 **비율 검정(Proportions Z-test)**과 **표본 크기 계산(Power Analysis)**을 배웁니다.
2. 데이터 준비
A/B 테스트 로그 데이터가 없으므로, 기존 데이터에서 성별을 A/B 그룹이라고 가정하고 시뮬레이션해봅니다.
- Group A: 남성 (Male)
- Group B: 여성 (Female)
- Conversion: 구매 여부 (주문 이력이 있으면 1, 없으면 0)
BigQuery (SQL)
from statsmodels.stats.proportion import proportions_ztest
import numpy as np
# ... BigQuery client setup3. 비율 검정 (Proportions Z-test)
전환율(Conversion Rate)과 같은 비율을 비교할 때 사용합니다.
❓ 문제 1: 그룹 간 전환율 비교
Q. 남성과 여성의 구매 전환율(전체 가입자 중 구매자 비율)을 구하고, 두 비율의 차이가 유의미한지 검정하세요.
BigQuery + Python
Hint: COUNT(DISTINCT user_id)로 전체 모수를 구하고, LEFT JOIN orders로 구매자를 셉니다.
정답 코드 보기
# 1. 데이터 집계
query = """
SELECT
u.gender,
COUNT(DISTINCT u.user_id) as total_users,
COUNT(DISTINCT o.user_id) as purchasers
FROM `your-project-id.retail_analytics_us.src_users` u
LEFT JOIN `your-project-id.retail_analytics_us.src_orders` o ON u.user_id = o.user_id
GROUP BY u.gender
"""
df = client.query(query).to_dataframe().set_index('gender')
# 2. 통계량 추출 (성공 횟수, 시행 횟수)
count = df['purchasers'].values # [남성구매자수, 여성구매자수]
nobs = df['total_users'].values # [남성전체, 여성전체]
# 3. Z-test
z_stat, p_val = proportions_ztest(count, nobs)
print(f"남성 전환율: {count[0]/nobs[0]:.4f}")
print(f"여성 전환율: {count[1]/nobs[1]:.4f}")
print(f"P-value: {p_val:.4f}")
if p_val < 0.05:
print("✅ 전환율 차이가 유의미합니다.")Error: name 'client' is not defined
4. 표본 크기 계산 (Sample Size & Power)
테스트를 하기 전에 가장 먼저 해야 할 질문입니다.
“몇 명한테 실험해야 믿을 만한 결과가 나오나요?”
너무 적으면 효과가 있어도 못 찾고(False Negative), 너무 많으면 돈 낭비입니다.
❓ 문제 2: 필요한 표본 수 계산
Q. 현재 전환율이 10%라고 할 때, 이를 11%로 개선(1%p 상승)하는 것을 감지하려면 그룹당 몇 명이 필요한가요? (유의수준 , 검정력 Power=0.8 기준)
Python (Common)
Hint: statsmodels.stats.power.NormalIndPower를 사용하거나 proportion_effectsize를 구해야 합니다.
정답 코드 보기
import statsmodels.stats.api as sms
from statsmodels.stats.proportion import proportion_effectsize
# 1. 효과 크기(Effect Size) 계산
p1 = 0.10 # 기존
p2 = 0.11 # 목표
effect_size = proportion_effectsize(p1, p2)
# 2. 표본 수 계산
required_n = sms.NormalIndPower().solve_power(
effect_size=effect_size,
power=0.8,
alpha=0.05,
ratio=1
)
print(f"필요한 그룹당 표본 크기: {int(np.ceil(required_n))}명")
print(f"총 필요 표본 크기: {int(np.ceil(required_n)) * 2}명")필요한 그룹당 표본 크기: 14745명 총 필요 표본 크기: 29490명
💡 파라미터 설명
- Alpha (): 1종 오류 확률 (보통 0.05). “효과 없는데 있다고 할 확률”
- Power (): 검정력 (보통 0.8). “효과가 있을 때 진짜 찾을 확률”
- Effect Size: 감지하고자 하는 차이의 크기 (클수록 적은 표본으로도 찾음)
5. 실험 설계 시 주의사항 (Common Pitfalls)
코딩만큼 중요한 것이 설계입니다.
-
Peeking Problem (엿보기):
- 실험 도중에 “어? P-value 0.04네요, 멈춥시다!”라고 하면 안 됩니다.
- 사전에 정한 표본 수(N)를 채울 때까지 기다려야 합니다.
-
SRM (Sample Ratio Mismatch):
- 50:50으로 나눴는데, 결과가 1000명 vs 950명이다?
- 트래픽 할당 시스템에 버그가 있거나, 특정 그룹에서 데이터 누락이 발생한 것입니다.
- 테스트 결과 무효!
💡 요약
- 비율 검정: 클릭률, 전환율 등 0/1 데이터 비교
- Power Analysis: 실험 시작 전 필수 단계 (“몇 명 필요해?”)
- A/B 테스트는 과학입니다: 감(Feeling)이 아니라 데이터로 의사결정하세요.
다음 챕터에서는 상관관계와 회귀분석을 통해 변수 간의 숨은 관계를 찾아봅니다.