A/B test
- 두 개의 그룹을 비교하여 차이가 있는지 판단
- A/B test, T-test… 등으로 불림
임의화(randomnization)
공정해야 한다
라는 것의 의미는?- 실험에서 고려한 의도적인 차이에 의한 효과(처리효과,treatment 효과)외에
- 다른 요인의 차이에 의해 두 그룹간에 효과의 차이가 나타날 가능성이 있으면 안된다는 것.
- ex) 신약의 효과를 입증하고자 하는 실험. 신약과 기존약의 차이외에 다른 요인이 없어야함.(인종,성별,나이,…)
- 두 그룹간의 비교가 공정하지 못하다면?
- 효과가 차이가 날 경우 진짜 의도한 차이에 의한 효과가 아니기에 과대평가 할 수 있음.
- 효과가 차이가 안날 수도 있는데 이 경우 과소평가.
- 두 경우 모두, 실험에서 고려한 의도적인 차이가 결과에 미치는 영향을 정확히 파악할 수 없음.
- 목적 : 두 그룹에 대한 비교는 반드시
공정
해야 함. 이때 공정함을 구현하는 방법. - 어떻게 구현?
- 두 개의 처리 중 하나를 실험대상에게 지정(배정)할때
- 처리의 배정을 임의로 결정(randomly assigned treatment) 하자.
- 평균적으로 처리 외의 다른 요인들이 두 그룹에서 유사하게 나타나게 할 수 있음.
- 두 그룹이 유사하기 때문에 처리외의 다른 요인으로 인한 효과의 차이가 나타날 가능성이 적음.
임의화 실험의 예시
임의화 구현
- 1874명의 사람들을 임상실험에 참가하는 환자들이라 가정하고 임의화 실험을 해보자
url = "https://ilovedata.github.io/teaching/bigdata2/data/physical_test_2018_data.csv"
physical_data = pd.read_csv(url)
twogroup = physical_data[["TEST_AGE","TEST_SEX","ITEM_F001","ITEM_F002"]].rename(columns = {"ITEM_F001":"height","ITEM_F002":"weight"})
twogroup
TEST_AGE | TEST_SEX | height | weight | |
---|---|---|---|---|
0 | 33 | M | 159.2 | 57.2 |
1 | 48 | F | 155.8 | 52.9 |
2 | 22 | M | 175.2 | 96.2 |
3 | 29 | M | 178.7 | 79.4 |
4 | 31 | F | 160.1 | 50.2 |
... | ... | ... | ... | ... |
1869 | 18 | M | 179.8 | 79.8 |
1870 | 29 | F | 156.3 | 51.4 |
1871 | 19 | M | 173.9 | 64.5 |
1872 | 66 | F | 141.3 | 54.9 |
1873 | 51 | F | 155.5 | 59.9 |
1874 rows × 4 columns
- 각 환자에게 A,B 중 어떤 약을 투약할지 임의로 지정!
array(['A', 'A', 'A', ..., 'A', 'A', 'A'], dtype='<U1')
TEST_AGE | TEST_SEX | height | weight | treatment | |
---|---|---|---|---|---|
0 | 33 | M | 159.2 | 57.2 | A |
1 | 48 | F | 155.8 | 52.9 | A |
2 | 22 | M | 175.2 | 96.2 | B |
3 | 29 | M | 178.7 | 79.4 | A |
4 | 31 | F | 160.1 | 50.2 | A |
5 | 23 | F | 157.8 | 60.1 | A |
6 | 11 | M | 165.5 | 60.3 | B |
7 | 24 | M | 174.9 | 74.5 | A |
8 | 18 | M | 181.0 | 71.3 | B |
9 | 41 | F | 160.6 | 72.7 | B |
- 임의화로 지정한 두 그룹에 대하여 나이,몸무게,키에 대한 분포 비교
TEST_AGE | height | weight | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | mean | std | min | 25% | 50% | 75% | max | count | mean | ... | 75% | max | count | mean | std | min | 25% | 50% | 75% | max | |
treatment | |||||||||||||||||||||
A | 934.0 | 35.616702 | 17.432484 | 11.0 | 21.0 | 29.0 | 50.75 | 84.0 | 934.0 | 167.455353 | ... | 174.5 | 195.2 | 934.0 | 67.466381 | 13.359358 | 34.1 | 57.925 | 66.85 | 75.300 | 132.3 |
B | 940.0 | 35.329787 | 16.892562 | 11.0 | 22.0 | 28.5 | 48.00 | 83.0 | 940.0 | 166.716064 | ... | 173.0 | 190.6 | 940.0 | 66.154362 | 12.926716 | 31.1 | 56.400 | 64.65 | 75.025 | 122.6 |
2 rows × 24 columns
<AxesSubplot:xlabel='TEST_AGE', ylabel='Count'>
<AxesSubplot:xlabel='weight', ylabel='Count'>
<AxesSubplot:xlabel='TEST_AGE', ylabel='Count'>
결과
- 처리를 제외한 다른 요인들의 분포가 각각의 그룹에서 유사하게 나타냄
- 처리를 제외한 다른 요인들로 인한 효과의 차이가 나타날 가능성 거의 없음.
- “실험에서 처리를
임의화를 통하여 배정
하면 두 그룹을공정하게 비교
할 수 있음”을 확인!
A/B test
- 두 그룹간의 차이가 있는지 비교.
- 유의성 검정을 통해 수행할 수 있음.
filename = "https://ilovedata.github.io/teaching/bigdata2/data/drug.csv"
three_drug_wide = pd.read_csv(filename)
three_drug_wide.head(10)
Placebo | Old | New | |
---|---|---|---|
0 | 31 | 23 | 23 |
1 | 28 | 17 | 17 |
2 | 34 | 29 | 11 |
3 | 36 | 23 | 11 |
4 | 33 | 17 | 9 |
5 | 27 | 17 | 16 |
6 | 39 | 22 | 16 |
7 | 25 | 17 | 14 |
8 | 23 | 23 | 17 |
9 | 29 | 25 | 18 |
Placebo | Old | New | |
---|---|---|---|
count | 20.000000 | 20.000000 | 20.000000 |
mean | 31.550000 | 20.900000 | 14.850000 |
std | 4.334379 | 5.990343 | 4.319783 |
min | 23.000000 | 5.000000 | 7.000000 |
25% | 28.750000 | 17.000000 | 11.000000 |
50% | 31.500000 | 21.000000 | 15.000000 |
75% | 34.500000 | 24.250000 | 17.000000 |
max | 39.000000 | 33.000000 | 24.000000 |
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Placebo 20 non-null int64
1 Old 20 non-null int64
2 New 20 non-null int64
dtypes: int64(3)
memory usage: 608.0 bytes
- pd.melt(frame,id_vars,value_vars,var_name,value_name)
- frame : long형으로 바꿀 df
- value_vars : value가 될 variable. 즉 값으로 바꿀 열들
- var_name : “variable” column이 이름. variable컬럼에는 column이름들이 포함되어 있음
- value_name : “value” column의 이름. value column에는 값들이 들어 있음.
three_drug_long = pd.melt(frame = three_drug_wide,value_vars = ["Placebo","Old","New"],var_name = "treatment",value_name = "Value")
three_drug_long.sample(10)
treatment | Value | |
---|---|---|
41 | New | 17 |
18 | Placebo | 30 |
46 | New | 16 |
17 | Placebo | 32 |
11 | Placebo | 36 |
55 | New | 17 |
48 | New | 17 |
51 | New | 7 |
0 | Placebo | 31 |
16 | Placebo | 30 |
cond = three_drug_long[three_drug_long.treatment == "Old"].index
two_drug = three_drug_long.drop(index=cond_col).reset_index(drop=True)
two_drug.head(10)
treatment | Value | |
---|---|---|
0 | Placebo | 31 |
1 | Placebo | 28 |
2 | Placebo | 34 |
3 | Placebo | 36 |
4 | Placebo | 33 |
5 | Placebo | 27 |
6 | Placebo | 39 |
7 | Placebo | 25 |
8 | Placebo | 23 |
9 | Placebo | 29 |
Value | ||||||||
---|---|---|---|---|---|---|---|---|
count | mean | std | min | 25% | 50% | 75% | max | |
treatment | ||||||||
New | 20.0 | 14.85 | 4.319783 | 7.0 | 11.00 | 15.0 | 17.0 | 24.0 |
Placebo | 20.0 | 31.55 | 4.334379 | 23.0 | 28.75 | 31.5 | 34.5 | 39.0 |
treatment | Value | |
---|---|---|
0 | New | 14.85 |
1 | Placebo | 31.55 |
x_p = float(temp[temp.treatment == "Placebo"].Value)
x_n = float(temp[temp.treatment == "New"].Value)
test_stat = x_p - x_n
test_stat
16.700000000000003
def diff_mean(df,group_label,treatments = None):
"""
df를 받아서 검정통계량을 계산해주는 함수. 검정통계량은 두 그룹간의 평균의 차이
df : dataframe
group_label : 처리가 적용된 변수명(그룹화 레이블)
treatments : 비교하고 싶은 두 개의 처리명
return : A 그룹 평균 - B 그룹 평균
"""
df_group = df.groupby(group_label)
temp = df_group.mean().reset_index()
test_stat = float(temp[temp[group_label] == treatments[0]].Value) - float(temp[temp[group_label] == treatments[1]].Value)
return test_stat
diff_mean(three_drug_long,"treatment",treatments = ["Placebo","New"])
16.700000000000003
treatment | Value | permuted_treatment | |
---|---|---|---|
0 | Placebo | 31 | New |
1 | Placebo | 28 | New |
2 | Placebo | 34 | Placebo |
3 | Placebo | 36 | Placebo |
4 | Placebo | 33 | Placebo |
5 | Placebo | 27 | Placebo |
6 | Placebo | 39 | New |
7 | Placebo | 25 | New |
8 | Placebo | 23 | Placebo |
9 | Placebo | 29 | New |
10 | Placebo | 36 | Placebo |
11 | Placebo | 36 | Placebo |
12 | Placebo | 32 | New |
13 | Placebo | 30 | New |
14 | Placebo | 33 | Placebo |
15 | Placebo | 39 | Placebo |
16 | Placebo | 30 | New |
17 | Placebo | 32 | Placebo |
18 | Placebo | 30 | New |
19 | Placebo | 28 | New |
20 | New | 23 | Placebo |
21 | New | 17 | New |
22 | New | 11 | New |
23 | New | 11 | New |
24 | New | 9 | New |
25 | New | 16 | New |
26 | New | 16 | Placebo |
27 | New | 14 | Placebo |
28 | New | 17 | Placebo |
29 | New | 18 | Placebo |
30 | New | 10 | New |
31 | New | 7 | Placebo |
32 | New | 14 | New |
33 | New | 24 | New |
34 | New | 18 | Placebo |
35 | New | 17 | Placebo |
36 | New | 14 | New |
37 | New | 14 | Placebo |
38 | New | 16 | Placebo |
39 | New | 11 | New |
treatment = two_drug.treatment
two_drug_permuted = two_drug
random_permuted_treatment = treatment.sample(frac=1.0,replace=False).reset_index(drop=True)
#reset_index안하면 sample로 shuffle을 해도 안섞임! 아마 인덱스끼리 매칭해주는 기능 있는 듯.
random_permuted_treatment.head(10)
0 New
1 New
2 Placebo
3 New
4 Placebo
5 New
6 Placebo
7 New
8 New
9 New
Name: treatment, dtype: object
two_drug_permuted = two_drug
two_drug_permuted["permuted_treatment"] = random_permuted_treatment
two_drug_permuted
treatment | Value | permuted_treatment | |
---|---|---|---|
0 | Placebo | 31 | New |
1 | Placebo | 28 | New |
2 | Placebo | 34 | Placebo |
3 | Placebo | 36 | New |
4 | Placebo | 33 | Placebo |
5 | Placebo | 27 | New |
6 | Placebo | 39 | Placebo |
7 | Placebo | 25 | New |
8 | Placebo | 23 | New |
9 | Placebo | 29 | New |
10 | Placebo | 36 | Placebo |
11 | Placebo | 36 | New |
12 | Placebo | 32 | Placebo |
13 | Placebo | 30 | Placebo |
14 | Placebo | 33 | New |
15 | Placebo | 39 | Placebo |
16 | Placebo | 30 | Placebo |
17 | Placebo | 32 | New |
18 | Placebo | 30 | Placebo |
19 | Placebo | 28 | Placebo |
20 | New | 23 | Placebo |
21 | New | 17 | Placebo |
22 | New | 11 | New |
23 | New | 11 | New |
24 | New | 9 | New |
25 | New | 16 | New |
26 | New | 16 | New |
27 | New | 14 | Placebo |
28 | New | 17 | New |
29 | New | 18 | Placebo |
30 | New | 10 | Placebo |
31 | New | 7 | New |
32 | New | 14 | New |
33 | New | 24 | Placebo |
34 | New | 18 | Placebo |
35 | New | 17 | New |
36 | New | 14 | Placebo |
37 | New | 14 | New |
38 | New | 16 | Placebo |
39 | New | 11 | Placebo |
B = 5000
test_stat_permuted = pd.DataFrame({"test_stat":np.zeros(B)})
treatment = two_drug.treatment
for i in np.arange(B):
random_permuted_treatment = treatment.sample(frac=1.0,replace=False).reset_index(drop=True)
two_drug_permuted = two_drug
two_drug_permuted["permuted_treatment"] = random_permuted_treatment
test_stat_permuted.loc[i,"test_stat"] = diff_mean(two_drug_permuted,"permuted_treatment",["Placebo","New"])