[Yammer/Mode] A/B ํ ์คํธ ๋ถ์ : 2๏ธโฃ ๋๋ค ์ํ๋ง
[Yammer/Mode] A/B ํ ์คํธ ๊ฒฐ๊ณผ ๊ฒ์ฆ : 1๏ธโฃ SQL ์ฟผ๋ฆฌ ๋ฏ์ด๋ณด๊ธฐYammer ๋ถ์ ํ๋ก์ ํธ"์ผ๋จธ(Yammer)"๋ ๋ง์ดํฌ๋ก์ํํธ ์ฐํ์ ๊ธฐ์ ์ฉ ์์ ๋คํธ์ํฌ ์๋น์ค ํ์ฌ์ ๋๋ค. Mode์์ ๊ฐ์์ ์ผ๋จธ์ฌ์ ๋ฐ์ดํฐ๋ฅผ
syimmin-data-analysis.tistory.com
์ด์ ํฌ์คํ ์์ ์ํ์ด ๋ฌด์์ํ๊ฒ ์ถ์ถ๋์๋์ง ํ์ธํ์๊ณ ์๋ก์ด ๋์กฐ์ง๋จ๊ณผ ์คํ์ง๋จ์ ์ค์ ํด์ฃผ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด์ ๋ชฉํ์งํ์๋ ์ ์ ๋น ํฌ์คํ ์๋ ์คํ์ ํจ๊ณผ๋ฅผ ์ธก์ ํ๊ธฐ์ ์ณ์ง ์๋ค๊ณ ํ๋จํ์ฌ ๋ก๊ทธ์ธ ๋๋น ํฌ์คํ ์ ํ์จ์ ์๋ก์ด ๋ชฉํ์งํ๋ก ์ค์ ํด์ฃผ์์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ์๋ก์ด ๋ชฉํ์งํ๋ฅผ ๊ณ์ฐํด๋ณด๊ณ ์ํ ์ฌ์ด์ฆ๊ฐ ์ ์ ํ์ง ํ์ธํ ํ, t-test๋ฅผ ์งํํ์ฌ ํต๊ณ์ ์ ์์ฑ์ ๊ฒ์ ํด์ฃผ๊ฒ ์ต๋๋ค.
๋ชฉํ์งํ ์ฌ์ค์ ํ๊ธฐ
์์ ํฌ์คํ ์์ ์ด๋ฐ ์๊ฐ์ด ๋ค๊ธฐ ์์ํฉ๋๋ค. "์คํ์ ๋ชฉํ์งํ๋ฅผ 1์ธ๋น ํฌ์คํ ์๋ก ์ค์ ํ๋ ๊ฒ ์ณ์๊ฐ?"
์ง๋จ์ engagement ์๋ ๋ค๋ฅผ ๋ฟ๋๋ฌ, ๋จ์ํ ํฌ์คํ ์๋ก ์๋ก์ด ํฌ์คํ ๊ธฐ๋ฅ์ด ๋ ํจ๊ณผ์ ์ด๋ผ๊ณ ํ ์ ์์๊น์?
๋ง์ฝ 1๋ฒ ๋ก๊ทธ์ธํ๊ณ 2๋ฒ ํฌ์คํ ํ ๊ฒ๊ณผ 4๋ฒ ๋ก๊ทธ์ธํ๊ณ 3๋ฒ ํฌ์คํ ํ ๊ฒ ์ค ์ด๋ค ๊ฒฝ์ฐ๊ฐ ๋ ์ค์ง์ ์ธ ๊ธฐ๋ฅ ๊ฐ์ ์ผ๋ก ๋ณผ ์ ์์๊น์?
๋ฐ๋ผ์ ์๋ก์ด ๊ฒ์ ๊ธฐ๋ฅ์ ํจ๊ณผ๋ฅผ ํ๊ฐํ๊ธฐ ์ํด์๋, ๋ก๊ทธ์ธ ์๋ฅผ ๋ฐ์ํ ํฌ์คํ ์ ํ์จ์ ํต์ฌ ์งํ๋ก ์ผ์์ผ ํฉ๋๋ค.
-- publisher update ์คํ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
with ex as (
SELECT user_id,
experiment,
experiment_group,
occurred_at
FROM tutorial.yammer_experiments
WHERE experiment = 'publisher_update'
),
-- ์คํ ์ฒ์น๋ฐ์ ๊ธฐ๊ฐ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
a as
(SELECT
ex.experiment_group,
ex.user_id,
e.event_name,
e.occurred_at,
case when event_name = 'login' then 1 else 0 end as session
FROM ex
JOIN tutorial.yammer_events e
ON e.user_id = ex.user_id
AND e.occurred_at >= ex.occurred_at
AND e.occurred_at < '2014-07-01'
AND e.event_type = 'engagement'
AND e.event_name in ('login', 'send_message')
order by 2, 4),
-- ๊ฐ login ๋ง๋ค ์ธ์
๋งํนํ๊ธฐ
b as
(SELECT
experiment_group,
user_id,
event_name,
occurred_at,
sum(session) over (partition by experiment_group, user_id order by occurred_at) as session_grp
from a
order by 2, 4),
-- login ์ธ์
์ send_message ์ฌ๋ถ ํ์ธํ๊ธฐ
-- ์ธ์
์์ login -> send_message ํ์ผ๋ฉด 1, login๋ง ํ์ผ๋ฉด 0์ผ๋ก ํ์
c as
(SELECT
experiment_group,
user_id,
case when count(*) > 1 then 1 else 0 end as conversion
FROM b
group by experiment_group, user_id, session_grp
order by 2)
-- ์คํ์ง๋จ๊ณผ ๋์กฐ์ง๋จ์ ํ๊ท conversion_rate ๊ตฌํ๊ธฐ
SELECT
d as
(SELECT
experiment_group,
count(distinct user_id) as users,
avg(conversion) as average,
STDDEV(conversion) AS stdev,
VARIANCE(conversion) AS variance
from c
group by 1
- ํ๋ฒ ๋ก๊ทธ์ธํ ๋๋ง๋ค ์ธ์ ํ๋๋ก ๋งํนํ์์ต๋๋ค.
- ํ ๋ก๊ทธ์ธ ์ธ์ ์ send_message๋ฅผ ํ์ผ๋ฉด 1(์ ํO)์ผ๋ก ํ์, send_message๋ฅผ ํ์ง ์์๋ค๋ฉด 0(์ ํX)์ผ๋ก ํ์ํ์ต๋๋ค.
- ๋จ, ํ ๋ก๊ทธ์ธ ์ธ์ ์ ์ฌ๋ฌ๋ฒ send_message๋ฅผ ํ ๊ฒฝ์ฐ์๋, ์ค๋ณต์ผ๋ก countํ์ง ์๊ณ 1๋ฒ์ ์ ํ์ผ๋ก ํ์ํ์ต๋๋ค.
- ์ง๋จ๋ณ ํ๊ท ์ ํ์จ์ ๊ตฌํ์ต๋๋ค.


๋ชฉํ์งํ ๊ฒฐ๊ณผ ํ์ธ

์คํ ๊ฒฐ๊ณผ, ๋์กฐ์ง๋จ์ ์ ํ์จ์ 0.55, ์คํ์ง๋จ์ 0.61๋ก, ์๋ก์ด ๊ธฐ๋ฅ ๋์ ์ ์ฝ 10%์ ์ ํ์จ ์ฆ๊ฐ๊ฐ ๋ํ๋ฌ์ต๋๋ค.
์ด์ ์งํ์์๋ 50%์ ์งํ ์์น๋ฅ ์ด ์์์ง๋ง ๋ชฉํ์งํ๋ฅผ ์ ํํ๊ณ ๋ ๋ค, ์์น๋ฅ ์ด ์กฐ์ ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ 1์ธ๋น ํฌ์คํ ์๋ณด๋ค ๋ก๊ทธ์ธ์์ ํฌ์คํ ๊น์ง์ ์ ํ์จ์ด ์๋ก์ด ํฌ์คํ ๊ธฐ๋ฅ์ ํจ๊ณผ๋ฅผ ์ ๋ํ๋ผ ์ ์์ต๋๋ค.
์ํ ์ฌ์ด์ฆ ์ค์

๋์กฐ์ง๋จ, ์คํ์ง๋จ์ ํ๋ณธ์ ์ ์ ์๋ 1081๋ช ์ผ๋ก ๋ ๋ง์ ํ๋ณธ์ ํ๋ณดํด์ผ ํจ์ ์ ์ ์์ต๋๋ค.
t-test ์งํํ๊ธฐ
- ์ดํ๋ณธ t-๊ฒ์ , ์์ธก ๊ฒ์ ์ผ๋ก ์งํ
- ๋ฑ๋ถ์ฐ์ด ์๋๊ธฐ ๋๋ฌธ์ Welch's t-test์ ์ํด ์์ ๋ ๊ณ์ฐ ํ p๊ฐ ๊ตฌํ๊ธฐ

-- ์ ์ฝ๋์์ CTE D ๊น์ง๋ ๋์ผํ๊ฒ ๊ฐ์ ธ์์ต๋๋ค
e as
(SELECT *,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.users ELSE NULL END) OVER () AS control_users,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.average ELSE NULL END) OVER () AS control_average,
-- MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.total ELSE NULL END) OVER () AS control_total,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.variance ELSE NULL END) OVER () AS control_variance,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.stdev ELSE NULL END) OVER () AS control_stdev,
SUM(d.users) OVER () AS total_treated_users
FROM d)
SELECT
experiment_group,
users,
average,
stdev,
ROUND((e.average - e.control_average) / SQRT((e.variance/e.users) + (e.control_variance/e.control_users)),4), -- t๊ฐ ๊ตฌํ๊ธฐ
WITH e AS (
SELECT *,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.users ELSE NULL END) OVER () AS control_users,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.average ELSE NULL END) OVER () AS control_average,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.variance ELSE NULL END) OVER () AS control_variance,
MAX(CASE WHEN d.experiment_group = 'control_group' THEN d.stdev ELSE NULL END) OVER () AS control_stdev,
SUM(d.users) OVER () AS total_treated_users
FROM d
)
SELECT
experiment_group,
users,
average,
stdev,
-- t๊ฐ ๊ณ์ฐ
ROUND((e.average - e.control_average) / SQRT((e.variance/e.users) + (e.control_variance/e.control_users)), 4) AS t_value,
-- ์์ ๋ ๊ณ์ฐ (Welch's formula)
ROUND(POWER((e.variance / e.users + e.control_variance / e.control_users), 2)
/(POWER(e.variance / e.users, 2) / (e.users - 1) + POWER(e.control_variance / e.control_users, 2) / (e.control_users - 1)), 2) AS degrees_of_freedom
from e

๊ณ์ฐ ๊ฒฐ๊ณผ,
t๊ฐ์ 2.4088, ์์ ๋๋ 1332.3์ด ๋์์ต๋๋ค.
ํด๋น ๊ฐ์ผ๋ก ํ์ด์ฌ์์ p-๊ฐ์ ๊ตฌํด์ฃผ๊ฒ ์ต๋๋ค.

p๊ฐ์ 0.0161๋ก 0.05๋ณด๋ค ์๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ณผ๊ฐ ํต๊ณ์ ์ผ๋ก ์ ์๋ฏธํ๋ค๊ณ ํด์ํ ์ ์์ต๋๋ค!
๊ฒฐ๊ณผ ์ ๋ฆฌ ๋ฐ ํด์
A/B test์ ๋ชฉํ์งํ๋ฅผ ํฌ์คํ ์ ํ์จ๋ก ์๋ก ์ค์ ํ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๊ทธ์ ๋ฐ๋ฅธ ์ํ ์ฌ์ด์ฆ ์ค์ ๊ณผ t-test๋ฅผ ์งํํ์ต๋๋ค.
t-test์์๋ ์์ ๋(ํ๋ณธ ์)์ ๋ฐ๋ฅธ p๊ฐ์ ๊ตฌํด์คฌ๋๋ฐ ์ฌ์ค n>=30 ์ด๋ฉด, z๋ถํฌ์ ๋งค์ฐ ๊ฐ๊น์ ์ด์ ๋ฐฉ๋ฒ์ผ๋ก p๊ฐ์ ๊ณ์ฐํด๋ ๋ฌด๋ฐฉํฉ๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก p๊ฐ์ด 0.016์ผ๋ก ์คํ์ง๋จ๊ณผ ๋์กฐ์ง๋จ์ ํฌ์คํ
์ ํ์จ ์ฐจ์ด๊ฐ ํต๊ณ์ ์ผ๋ก ์ ์๋ฏธํ๋ค๊ณ ๋ณผ ์ ์์ต๋๋ค.


์ด์ ๋ชฉํ์งํ์ธ ํฌ์คํ
์์ ๊ฒฐ๊ณผ๊ฐ ์ฐจ์ด๋ 50% ์ด์ ์ฐจ์ด๋ฅผ ๋ณด์ธ ๋ฐ๋ฉด์, ์๋ก์ด ๋ชฉํ์งํ์ธ ์ ํ์จ๋ 10%์ ์ฐจ์ด๋ฅผ ๋ณด์์ต๋๋ค.
๊ธฐ์กด ์งํ๋ ์ค์ ์ ์ ํ๋์ด๋ ์๋น์ค์ ์ค์ง ๊ฐ์น๋ฅผ ์ ๋ํ๋ด์ง ๋ชปํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ ํ์จ์ Yammer์์ ์ ์ ๊ฐ ์ผ๋ง๋ ์์ฃผ ๋ก๊ทธ์ธํ๋์ง์ ์๊ด์์ด ์ค์ ๋ก ์๋น์ค๋ฅผ ์ฐ๋์ง ๋ ์ ๋ณด์ฌ์ฃผ๊ณ ์์ต๋๋ค.
์ ํ์จ ์ธ์๋ ๋ก๊ทธ์ธ ๋น๋๋ ํฌ์คํ ๋ง๊ณ ๋ค๋ฅธ ๊ธฐ๋ฅ(๋๊ธ, ์๋ฆผ, ์ข์์ ๋ฑ) ์ฌ์ฉ ๋น๋๋ ๊ฐ์ด ํ์ธํ์ฌ, ์ ํฌ์คํ ๊ธฐ๋ฅ์ด ์ ์ฒด์ ์ธ ์๋น์ค ํ๋ฆ์ ๋ถ์ ์ ์ธ ์ํฅ์ ์ฃผ์ง ์์๋์ง๋ ์ดํด๋ณผ ์ ์์ต๋๋ค.
ํ๊ณ์ ์ ์คํ ์ด์ ์ธ 5์ ๊ธฐ์ค์ผ๋ก ์คํ๊ตฐ๊ณผ ๋์กฐ๊ตฐ์ ์ ํ์จ์ด ๋์ผํ์ง ์์์ต๋๋ค. ์ด ๋ถ๋ถ์ด ๋์ค์ ๊ฒฐ๊ณผ ํด์์ ์ํฅ์ ์ค ์ ์๋ค๋ ์ ์์ ์์ฌ์ ๊ณ , ์คํ์ ์ฌ์ค๊ณํ๊ฒ ๋๋ค๋ฉด ํด๋น ๋ถ๋ถ์ ํต์ ํ ์ ์๋๋ก ์ค๊ณํ๋ฉด ์ข๊ฒ ์ต๋๋ค.
๋๋ ์
์คํ์์ ์ ์๋ฏธํ ์ธ์ฌ์ดํธ๋ฅผ ์ป๊ธฐ ์ํด์๋ ์คํ์ ์ ์ค๊ณํด์ผ ํ๋ค๋ ๊ฒ์ ๋๊ผ์ต๋๋ค.
์ธ๋ถ๋ณ์๊ฐ ํต์ ๋์ง ์์ผ๋ฉด ์คํ ์ฒ์น์ ์ค์ ํจ๊ณผ๋ฅผ ํด์ํ๊ธฐ ํ๋ค๋ฉฐ
์คํ์ ์ฑ๊ณต๊ธฐ์ค์ธ ๋ชฉํ์งํ๋ฅผ ์ ๋๋ก ์ค์ ํ์ง ์์ผ๋ฉด ์๋ฏธ์๋ ๊ฒฐ๊ณผ๋ฅผ ์ป๊ฒ ๋ ์ ์์ต๋๋ค.
์ธ๋ถ๋ณ์ ํต์ ๋ถํฐ ์งํ์ค์ ๊น์ง ์ฒด๊ณ์ ์ผ๋ก A/B test๋ฅผ ์ค๊ณํด์ผ ํ ๊ฒ์
๋๋ค.