01. ๊ฐ์ค ๊ฒ์ (Hypothesis Testing)
1. ๊ฐ์ ๋ฐ ์๋๋ฆฌ์ค
์ํฉ: โ๋จ์ฑ ๊ณ ๊ฐ์ด ์ฌ์ฑ ๊ณ ๊ฐ๋ณด๋ค ๋์ ๋ ๋ง์ด ์ด๋คโ๋ ์๋ฌธ์ด ํ ๋ด์ ๋๊ณ ์์ต๋๋ค. ๋ฐ์ดํฐ ํ์ฅ๋์ด ๋ฌป์ต๋๋ค.
โ๊ทธ๊ฑฐ ์ง์ง์ผ? ์๋๋ฉด ๊ทธ๋ฅ ์ฐ์ฐํ ๋ช๋ช ํฐ์ ๋จ์ฑ ๊ณ ๊ฐ ๋๋ฌธ ์๋์ผ? ํต๊ณ์ ์ผ๋ก ์ ์๋ฏธํ๊ฐ?โ
์ฐ๋ฆฌ๋ ๋จ์ํ ํ๊ท ์ ๋น๊ตํ๋ ๊ฒ์ ๋์ด, ๊ทธ ์ฐจ์ด๊ฐ ์ฐ์ฐ์ด ์๋์ ์ฆ๋ช ํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด T-test์ ANOVA๋ฅผ ์ฌ์ฉํฉ๋๋ค.
2. ๋ฐ์ดํฐ ์ค๋น
src_users์ src_orders, src_order_items ํ
์ด๋ธ์ ์ฌ์ฉํ์ฌ ๊ณ ๊ฐ๋ณ ์ด ๊ตฌ๋งค ๊ธ์ก์ ๊ตฌํฉ๋๋ค.
BigQuery (SQL)
from google.cloud import bigquery
from scipy import stats
import pandas as pd
client = bigquery.Client()3. T-Test: ๋ ์ง๋จ ๊ฐ ๋น๊ต (๋จ์ฑ vs ์ฌ์ฑ)
๊ฐ์ฅ ํํ ์ง๋ฌธ์ ๋๋ค. โA์ B์ ํ๊ท ์ด ๋ค๋ฅธ๊ฐ?โ
โ ๋ฌธ์ 1: ์ฑ๋ณ ํ๊ท ๊ตฌ๋งค ๊ธ์ก ๋น๊ต
Q. Independent Two-sample T-test๋ฅผ ์ํํ์ฌ ๋จ์ฑ๊ณผ ์ฌ์ฑ ๊ณ ๊ฐ์ ํ๊ท ์ด ๊ตฌ๋งค ๊ธ์ก ์ฐจ์ด๊ฐ ์ ์๋ฏธํ์ง ๊ฒ์ ํ์ธ์.
(๋จ, ๊ตฌ๋งค ์ด๋ ฅ์ด ์๋ ๊ณ ๊ฐ๋ง ๋์์ผ๋ก ํฉ๋๋ค.)
BigQuery + Python
์ ๋ต: BigQuery๋ก ๋ฐ์ดํฐ๋ฅผ ์ง๊ณํ์ฌ Python์ผ๋ก ๊ฐ์ ธ์จ ํ, scipy.stats๋ฅผ ์ฌ์ฉํฉ๋๋ค.
Hint: SQL์์ GROUP BY user_id, gender๋ก ์ด ๊ตฌ๋งค์ก์ ๊ตฌํ ๋ค ๊ฐ์ ธ์ต๋๋ค.
์ ๋ต ์ฝ๋ ๋ณด๊ธฐ
# 1. ๋ฐ์ดํฐ ์ถ์ถ
query = """
SELECT
u.gender,
SUM(oi.sale_price) as total_purchase
FROM `your-project-id.retail_analytics_us.src_users` u
JOIN `your-project-id.retail_analytics_us.src_orders` o ON u.user_id = o.user_id
JOIN `your-project-id.retail_analytics_us.src_order_items` oi ON o.order_id = oi.order_id
WHERE o.status NOT IN ('Cancelled', 'Returned')
GROUP BY u.user_id, u.gender
"""
df = client.query(query).to_dataframe()
# 2. ๊ทธ๋ฃน ๋ถ๋ฆฌ
group_m = df[df['gender'] == 'M']['total_purchase']
group_f = df[df['gender'] == 'F']['total_purchase']
# 3. T-test ์ํ (๋ฑ๋ถ์ฐ ๊ฐ์ X -> Welch's t-test)
t_stat, p_val = stats.ttest_ind(group_m, group_f, equal_var=False)
print(f"๋จ์ฑ ํ๊ท : ${group_m.mean():.2f}")
print(f"์ฌ์ฑ ํ๊ท : ${group_f.mean():.2f}")
print(f"T-statistic: {t_stat:.4f}")
print(f"P-value: {p_val:.4f}")
if p_val < 0.05:
print("โ
์ฐจ์ด๊ฐ ํต๊ณ์ ์ผ๋ก ์ ์๋ฏธํฉ๋๋ค.")
else:
print("โ ์ฐจ์ด๊ฐ ์ ์๋ฏธํ์ง ์์ต๋๋ค.")๋จ์ฑ ํ๊ท : $130.20 ์ฌ์ฑ ํ๊ท : $114.75 T-statistic: 15.4704 P-value: 0.0000 โ ์ฐจ์ด๊ฐ ํต๊ณ์ ์ผ๋ก ์ ์๋ฏธํฉ๋๋ค.
๐ก ํด์ ๊ฐ์ด๋
- P-value < 0.05: โ๋ ์ง๋จ์ ํ๊ท ์ด ๋ค๋ฅด๋คโ๋ ๊ฐ์ค์ ์ฑํ (์ ์๋ฏธํ ์ฐจ์ด).
- P-value >= 0.05: ์ฐจ์ด๊ฐ ์๋ค๊ณ ๋งํ ์ถฉ๋ถํ ์ฆ๊ฑฐ๊ฐ ์์ (์ฐ์ฐ์ผ ์ ์์).
- ๋จ์ฑ์ ํ๊ท ๊ตฌ๋งค์ก์ด ๋ ๋๊ณ P-value๊ฐ 0.0000์ด๋ผ๋ฉด, โ๋จ์ฑ์ด ํต๊ณ์ ์ผ๋ก ๋ ๋ง์ด ์ด๋คโ๊ณ ๊ฒฐ๋ก ๋ด๋ฆด ์ ์์ต๋๋ค.
4. ANOVA: ์ธ ๊ฐ ์ด์ ์ง๋จ ๋น๊ต
๋ง์ฝ ๋น๊ต ๋์์ด ์ ์ด์์ด๋ผ๋ฉด? (์: ๊ฐ์ ์ฑ๋๋ณ, ๊ตญ๊ฐ๋ณ) ์ด๋๋ T-test๋ฅผ ์ฌ๋ฌ ๋ฒ ํ๋ ๋์ **ANOVA(๋ถ์ฐ๋ถ์)**๋ฅผ ์ฌ์ฉํฉ๋๋ค.
โ ๋ฌธ์ 2: ๊ฐ์ ์ฑ๋(Traffic Source)๋ณ ๊ตฌ๋งค ๊ธ์ก ์ฐจ์ด
Q. traffic_source (Search, Displays, Facebook ๋ฑ)์ ๋ฐ๋ผ ๊ณ ๊ฐ๋ค์ ํ๊ท ์ด ๊ตฌ๋งค ๊ธ์ก์ ์ฐจ์ด๊ฐ ์๋์ง ๊ฒ์ ํ์ธ์.
BigQuery + Python
Hint: src_users ํ
์ด๋ธ์ traffic_source ์ปฌ๋ผ์ด ์์ผ๋ฏ๋ก, ์ ์
์ฑ๋ ๋ฐ์ดํฐ๊ฐ ํ์ํฉ๋๋ค.
์ฌ๊ธฐ์๋ src_users์ traffic source ์ ๋ณด๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๊ฑฐ๋, ์ฐ์ต์ ์ํด country๋ฅผ ์ฌ์ฉํด๋ด
์๋ค.
(๊ฐ์ด๋ ์์ : ๋ฐ์ดํฐ์
ํ๊ณ๋ก Country ๋น๊ต๋ก ๋์ฒดํฉ๋๋ค.)
๋์ฒด ๋ฌธ์ : ๊ตญ๊ฐ(Country)๋ณ ํ๊ท ๊ตฌ๋งค ๊ธ์ก ์ฐจ์ด ๊ฒ์
์ ๋ต ์ฝ๋ ๋ณด๊ธฐ
# 1. ๋ฐ์ดํฐ ์ถ์ถ
query = """
SELECT
u.country,
SUM(oi.sale_price) as total_purchase
FROM `your-project-id.retail_analytics_us.src_users` u
JOIN `your-project-id.retail_analytics_us.src_orders` o ON u.user_id = o.user_id
JOIN `your-project-id.retail_analytics_us.src_order_items` oi ON o.order_id = oi.order_id
WHERE o.status NOT IN ('Cancelled', 'Returned')
AND u.country IN ('United States', 'China', 'Japan') -- 3๊ฐ๊ตญ ๋น๊ต
GROUP BY u.user_id, u.country
"""
df_anova = client.query(query).to_dataframe()
# 2. ๊ทธ๋ฃน๋ณ ๋ฐ์ดํฐ ์ค๋น
groups = [df_anova[df_anova['country'] == c]['total_purchase'] for c in df_anova['country'].unique()]
# 3. ANOVA ์ํ
f_stat, p_val = stats.f_oneway(*groups)
print(f"F-statistic: {f_stat:.4f}")
print(f"P-value: {p_val:.4f}")F-statistic: 2.6464 P-value: 0.0709
5. ์ฌํ ๊ฒ์ (Post-hoc Analysis)
ANOVA์์ โ์ฐจ์ด๊ฐ ์๋ค(P < 0.05)โ๊ณ ๋์๋ค๋ฉด, **โ๊ตฌ์ฒด์ ์ผ๋ก ์ด๋๋ ์ด๋๊ฐ ๋ค๋ฅธ๋ฐ?โ**๋ฅผ ์์์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด Tukey HSD ํ ์คํธ๋ฅผ ํฉ๋๋ค.
โ ๋ฌธ์ 3: ๊ตญ๊ฐ๋ณ ์ฌํ ๊ฒ์
Q. statsmodels์ pairwise_tukeyhsd๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ ๊ตญ๊ฐ ๊ฐ์ ์ฐจ์ด๊ฐ ๋๋ ทํ์ง ํ์ธํ์ธ์.
Python (Common)
์ ๋ต ์ฝ๋ ๋ณด๊ธฐ
from statsmodels.stats.multicomp import pairwise_tukeyhsd
# df_anova๋ ์์์ ๋ง๋ ๋ฐ์ดํฐํ๋ ์
tukey = pairwise_tukeyhsd(endog=df_anova['total_purchase'],
groups=df_anova['country'],
alpha=0.05)
print(tukey)Multiple Comparison of Means - Tukey HSD, FWER=0.05 =========================================================== group1 group2 meandiff p-adj lower upper reject ----------------------------------------------------------- China Japan 6.4094 0.1268 -1.3222 14.141 False China United States -1.217 0.6424 -4.398 1.9641 False Japan United States -7.6263 0.0596 -15.4896 0.2369 False -----------------------------------------------------------
๊ฒฐ๊ณผ ํด์:
reject์ปฌ๋ผ์ดTrue์ธ ํ: ๋ ๊ทธ๋ฃน ๊ฐ ์ฐจ์ด๊ฐ ์ ์๋ฏธํจ.- ์: US vs China๊ฐ True๋ผ๋ฉด, ๋ ๊ตญ๊ฐ ๊ฐ ์๋น ์ฑํฅ์ด ๋ค๋ฆ.
๐ก ์์ฝ
- T-test: ๋ ์ง๋จ ๋น๊ต (A vs B)
- ANOVA: ์ ์ด์ ์ง๋จ ๋น๊ต (A vs B vs C)
- Post-hoc: ๋๊ฐ ๋ค๋ฅธ์ง ๋ฒ์ธ ์ฐพ๊ธฐ
๋ค์ ์ฑํฐ์์๋ A/B ํ ์คํธ๋ฅผ ํตํด ๋ง์ผํ ์ฑ๊ณผ๋ฅผ ๊ฒ์ฆํด๋ด ๋๋ค.