목차
분산분석
1) 개념
ANOVA : Analysis of Variance
분산은 변수에 있는 숫자들 사이의 퍼짐을 측정하는 것이다. 어떤 숫자가 평균과 변수의 모든 숫자로부터 얼마나 떨어져있는지 측정할 수 있다. 분산이 작으면 해당 변수는 종속변수에 큰 영향이 없다고 판단을 내릴 수 있다.
분산분석은 '분산'의 특징을 이용한 분석방법이다. 두 개 이상의 다수 집단 간 평균에 차이가 있는지 분석할 때 분산을 이용한다. 앞선 ttest가 두 집단간의 평균 차이를 비교한다면, 분산분석은 비교하고자 하는 것이 다수(2,3,4개...) 라는 관점에 차이가 있다. 분산분석은 ttest의 업그레이드 버전이라고 봐도 무방하다.
2) 방법
여러 집단 간 평균이 같은지 다른지 통계적 검정을 쓰기 위해 F값을 구한다.
그룹 간 차이(분산), 그룹 내 차이(분산) 두 가지를 이용한다. 구체적으로는
집단 내 분산< 집단 간 분산
이 더 크다면, 통계적 차이가 유의하다는 결론을 내리는 것이다.
3) 종류
종속변수가 1개일 때, 단일변량 분산분석이라 한다.
여기서 독립변수의 개수에 따라 일원배치, 이원배치, 다원배치로 나뉜다.
종속변수가 2개 이상일 때, 다변량 분산분석이라 한다.
종속변수 개수 | 독립변수 개수 | ||
1개 | 1 | 일원배치 분산분석 | One-way Anova |
2 | 이원배치 분산분석 | Two-way Anova | |
3 | 다원배치 분산분석 | Manova | |
2개 | 1개 이상 | 다변량 분산분석 |
1. One-way ANOVA 일원배치 분산분석
여러 개의 집단에서 하나의 범주형 변수의 영향이 있는지 알아보는 검증 방법이다.
모집단의 수에는 제한이 없고, 각 집단의 표본 수는 같지 않아도 된다.
♡ F 통계량
식: 집단 간 평균 제곱 / 집단 내 평균 제곱
집단 간 분산이 크면 F가 커진다.
집단 내 분산이 작으면 F가 커진다.
따라서 F통계량 값이 크다는 것은, 집단 간의 차이가 있다고 할 근거가 확실해지므로 귀무가설을 기각할 확률이 높아진다는 것을 알 수 있다.
♡ 가정
- 집단은 서로 독립적이다.
- 정규성을 만족한다.
- 등분산성을 만족한다.
♡ 가설
H0: k 개의 집단 간 모평균 차이는 없다.
H1: k 개의 집단 간 모평균이 다르다.
♡ 사후검정
분산분석 결과 귀무가설이 기각되어, 적어도 한 집단에서 평균의 차이가 있다는 것이 통계적으로 증명된 후, 어떤 집단들에 대해서 평균의 차이가 존재하는지 알아본다.
< 사후검정 종류 >
1) 튜키의 HSD
2) 던칸의 MRT
3) vltudml LSD
♡ ANOVA 파이썬 코드 예제
*** 그룹 간 차이가 없을 때
1) 데이터 생성하기
# 필수 라이브러리
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as stats
# 데이터 생성
np.random.seed(12)
races = ["asian","black","hispanic","other","white"]
# Generate random data
voter_race = np.random.choice(a= races,
p = [0.05, 0.15 ,0.25, 0.05, 0.5],
size=1000)
voter_age = stats.poisson.rvs(loc=18,
mu=30,
size=1000)
# Group age data by race
voter_frame = pd.DataFrame({"race":voter_race,"age":voter_age})
voter_frame
race | age |
black | 57 |
black | 51 |
white | 49 |
hispanic | 51 |
white | 48 |
asian | 56 |
white | 40 |
white | 51 |
asian | 52 |
★ numpy.random.choice(a, size, replace, p)
a : 1차원 배열, 샘플을 추출할 대상
size : 샘플 크기
p : 배열 내 각 요소가 선택될 확률
★ stats.poisson.rvs(loc, mu, size)
포아송 분포로부터 랜덤 샘플을 생성하는 함수이다.
loc : 분포의 시작점
mu : 평균
size : 샘플 크기
2) 데이터 집단 살펴보기
# 어떤 집단인지 보기
groups = voter_frame.groupby("race").groups
groups
{'asian': Int64Index([ 4, 7, 14, 21, 49, 53, 59, 78, 95, 98, 135, 136, 162,
203, 227, 264, 278, 289, 326, 335, 345, 373, 430, 480, 484, 491,
516, 587, 602, 684, 692, 708, 715, 761, 776, 826, 828, 832, 853,
897, 942, 951, 986, 996],
dtype='int64'),
'black': Int64Index([ 0, 9, 19, 22, 23, 42, 50, 56, 62, 76,
...
948, 956, 961, 965, 968, 972, 982, 984, 989, 990],
dtype='int64', length=147),
'hispanic': Int64Index([ 2, 10, 24, 28, 31, 32, 38, 40, 44, 45,
...
954, 955, 958, 959, 962, 964, 966, 974, 994, 999],
dtype='int64', length=244),
'other': Int64Index([ 17, 26, 39, 46, 48, 65, 67, 72, 146, 237, 246, 255, 284,
302, 317, 322, 358, 370, 386, 413, 425, 446, 530, 542, 569, 571,
573, 575, 583, 626, 629, 637, 662, 696, 700, 701, 728, 739, 756,
757, 773, 813, 819, 880, 923, 936, 939, 971, 980, 992],
dtype='int64'),
'white': Int64Index([ 1, 3, 5, 6, 8, 11, 12, 13, 15, 16,
...
981, 983, 985, 987, 988, 991, 993, 995, 997, 998],
dtype='int64', length=515)}
# 카테고리컬 데이터, 5개의 집단 분류하기
black_voter=voter_frame[voter_frame['race']=='black'].age
asian_voter = voter_frame[voter_frame['race']=='asian'].age
hispanic_voter = voter_frame[voter_frame['race']=='other'].age
other_voter = voter_frame[voter_frame['race']=='hispanic'].age
white_voter=voter_frame[voter_frame['race']=='white'].age
3) 가설설정
H0: 인종별 유권자 나이 평균은 모두 같다 = 그룹 간 평균에 차이가 없다.
H1: 인종별 유권자 나이 평균은 적어도 하나는 다르다. = 최소한 하나의 그룹의 평균이 다른 그룹과 다르다.
4) ANOVA test
★ stats.f_one_way()
# Perform the ANOVA
stats.f_oneway(asian_voter, black_voter, hispanic_voter, other_voter, white_voter)
F_onewayResult(statistic=1.7744689357329695, pvalue=0.13173183201930463)
f통계값은 1.774이고 pvalue는 0.1317이다. 유의수준 0.05 아래, p value값이 높으므로 귀무가설을 기각하지 않는다.
즉, 모든 그룹의 평균에 차이가 없으니 인종별 유권자 나이 평균은 모두 같다.
import statsmodels.api as sm
from statsmodels.formula.api import ols
# model formula--> fit()
model = ols('age ~ race',
data = voter_frame).fit()
anova_result = sm.stats.anova_lm(model, typ=2)
print (anova_result)
sum_sq df F PR(>F)
race 199.369 4.0 1.774469 0.131732
Residual 27948.102 995.0 NaN NaN
*** 그룹 간 차이가 있을 때
1) 데이터 생성하기
np.random.seed(12)
# Generate random data
voter_race = np.random.choice(a= races,
p = [0.05, 0.15 ,0.25, 0.05, 0.5],
size=1000)
# white_ages는 데이터 분포가 다르게 설정(평균이 32)
white_ages = stats.poisson.rvs(loc=18,
mu=32,
size=1000)
voter_age = stats.poisson.rvs(loc=18,
mu=30,
size=1000)
voter_age = np.where(voter_race=="white", white_ages, voter_age)
# Group age data by race
voter_frame = pd.DataFrame({"race":voter_race,"age":voter_age})
groups = voter_frame.groupby("race").groups
# Extract individual groups
asian = voter_age[groups["asian"]]
black = voter_age[groups["black"]]
hispanic = voter_age[groups["hispanic"]]
other = voter_age[groups["other"]]
white = voter_age[groups["white"]]
# Perform the ANOVA
stats.f_oneway(asian, black, hispanic, other, white)
★ where(x, a, b)
x = 조건
a = 조건이 참일 때
b = 조건이 거짓일 때
2) 데이터 집단 살펴보기
voter_frame = pd.DataFrame({"race":voter_race,"age":voter_age})
groups = voter_frame.groupby("race").groups
{'asian': Int64Index([ 4, 7, 14, 21, 49, 53, 59, 78, 95, 98, 135, 136, 162,
203, 227, 264, 278, 289, 326, 335, 345, 373, 430, 480, 484, 491,
516, 587, 602, 684, 692, 708, 715, 761, 776, 826, 828, 832, 853,
897, 942, 951, 986, 996],
dtype='int64'),
'black': Int64Index([ 0, 9, 19, 22, 23, 42, 50, 56, 62, 76,
...
948, 956, 961, 965, 968, 972, 982, 984, 989, 990],
dtype='int64', length=147),
'hispanic': Int64Index([ 2, 10, 24, 28, 31, 32, 38, 40, 44, 45,
...
954, 955, 958, 959, 962, 964, 966, 974, 994, 999],
dtype='int64', length=244),
'other': Int64Index([ 17, 26, 39, 46, 48, 65, 67, 72, 146, 237, 246, 255, 284,
302, 317, 322, 358, 370, 386, 413, 425, 446, 530, 542, 569, 571,
573, 575, 583, 626, 629, 637, 662, 696, 700, 701, 728, 739, 756,
757, 773, 813, 819, 880, 923, 936, 939, 971, 980, 992],
dtype='int64'),
'white': Int64Index([ 1, 3, 5, 6, 8, 11, 12, 13, 15, 16,
...
981, 983, 985, 987, 988, 991, 993, 995, 997, 998],
dtype='int64', length=515)}
# 개별 집단 추출하기
asian = voter_age[groups["asian"]]
black = voter_age[groups["black"]]
hispanic = voter_age[groups["hispanic"]]
other = voter_age[groups["other"]]
white = voter_age[groups["white"]]
3) 가설설정
H0: 인종별 유권자 나이 평균은 모두 같다 = 그룹 간 평균에 차이가 없다.
H1: 인종별 유권자 나이 평균은 적어도 하나는 다르다. = 최소한 하나의 그룹의 평균이 다른 그룹과 다르다.
4) ANOVA test
★ stats.f_one_way()
# Perform the ANOVA
stats.f_oneway(asian, black, hispanic, other, white)
F_onewayResult(statistic=10.164699828386366, pvalue=4.5613242113994585e-08)
★ sm.stats.anvoa_lm()
model = ols('age ~ race',
data = voter_frame).fit()
anova_result = sm.stats.anova_lm(model, typ=2)
print (anova_result)
sum_sq df F PR(>F)
race 1284.123213 4.0 10.1647 4.561324e-08
Residual 31424.995787 995.0 NaN NaN
F값이 10.1647, 유의수준 0.05 아래 pvalue가 매우 작기 때문에 귀무가설을 기각하고 대립가설을 채택한다. 즉, 인종별 평균 나이는 적어도 하나 이상이 다르기 때문에, 여러 그룹 간의 평균에 차이가 있다.
5) 사후 검정 Post-hoc Test
ANOVA 결과가 유의미한 결과가 나온 후 진행하는 test이다.
★ 필요성?
ANOVA는 여러 그룹 간 평균의 차이가 통계쩍으로 유의미한지만 알려줄 뿐, 어떤 그룹들이 차이가 있는지는 알려주지 않는다. 어떤 그룹들 간에 차이가 있는지 구체적으로 알아보기 위해 사후검정을 실시한다.
★ 방법?
ttest
stats.ttest_ind()
두 그룹 간의 평균 차이를 검정한다.
각 그룹 별로 쌍을 만들어 ttest로 비교하여 두 그룹 간 유의미한 차이가 있는지 확인한다.
# 2개씩 묶기
race_pairs = []
for race1 in range(4):
for race2 in range(race1+1,5):
race_pairs.append((races[race1], races[race2]))
# 반복문 이용, 짝별로 ttest 시행하기
for race1, race2 in race_pairs:
print(race1, race2)
print(stats.ttest_ind(voter_age[groups[race1]],
voter_age[groups[race2]]))
asian black
Ttest_indResult(statistic=0.838644690974798, pvalue=0.4027281369339345)
asian hispanic
Ttest_indResult(statistic=-0.42594691924932293, pvalue=0.6704669004240726)
asian other
Ttest_indResult(statistic=0.9795284739636, pvalue=0.3298877500095151)
asian white
Ttest_indResult(statistic=-2.318108811252288, pvalue=0.020804701566400217)
black hispanic
Ttest_indResult(statistic=-1.9527839210712925, pvalue=0.05156197171952594)
black other
Ttest_indResult(statistic=0.28025754367057176, pvalue=0.7795770111117659)
black white
Ttest_indResult(statistic=-5.379303881281835, pvalue=1.039421216662395e-07)
hispanic other
Ttest_indResult(statistic=1.5853626170340225, pvalue=0.11396630528484335)
hispanic white
Ttest_indResult(statistic=-3.5160312714115376, pvalue=0.0004641298649066684)
other white
Ttest_indResult(statistic=-3.763809322077872, pvalue=0.00018490576317593065)
>>pvalue가 0.05 이하로 나타나면, 해당 그룹의 평균이 다른 그룹과 유의미하게 다를 가능성이 높다는 뜻을 의미한다.
TukeyHSD 투키의 사후검정
pairwise_tukeyhsd()
다중 비교 문제를 해결하기 위한 방법이다.
* 수평선 : 각 그룹 간 평균 차이를 나타내는 신뢰구간, 신뢰 구간의 너비가 좁을 수록 평균 차이의 추정치에 대한 확신이 높다. 양수인 경우 한 그룹의 평균이 다른 그룹보다 높다는 것을 뜻한다. 신뢰구간이 겹치는 경우 두 그룹 간의 차이가 통계적으로 유의미하지 않을 수 있다는 것을 의미한다.
* 점 : 그룹 간 평균 차이의 추정치
from statsmodels.stats.multicomp import pairwise_tukeyhsd
tukey = pairwise_tukeyhsd(endog=voter_age, # 데이터
groups=voter_race, # 비교할 그룹
alpha=0.05) # 유의수준
tukey.plot_simultaneous() # Plot group confidence intervals
plt.vlines(x=49.57,ymin=-0.5,ymax=4.5, color="red")
★ endog: 그룹 간에 비교되는 변수를 지정
★ group: 비교 중인 그룹
★ alpha: 함수에 유의 수준 또는 신뢰 수준, 일반적인 95% 신뢰 수준을 목표
tukey.summary()
2. Two-way ANOVA 이원배치 분산분석
하나의 종속변수에 대한 두 개의 독립변수의 영향을 알아보기 위한 검증 방법이다.
두 독립변수에 대한 교호작용 검증이 반드시 있어야 한다.
(두 독립변수의 범주들의 조합으로 인해 종속변수에 미치는 영향이 있는지에 대한 검증이다.)
예시문제
비료 종류와 식재 밀도가 작물 수확량에 미치는 영향
레드팜오일과 성별이 말라리아 유병률에 미치는 영향
고단백 아침식사와 성별이 청소년 학습능력에 미치는 영향
♡ 가정
1) 집단은 서로 독립적이다.
2) 정규성을 만족한다.
3) 등분산성을 만족한다.
♡ 가설(총 6개가 나옴)
< 교호작용 검정 >
두 개 이상의 변수가 함께 작용할 때, 두 변수들이 서로 영향을 주고받는 상호작용에 관한 것이다.
교호작용 검정(Interaction effect)는 한 독립변수가 종속변수에 미치는 영향이 다른 독립변수에 따라 달라지는 지 분석하는 것이다.
H0: 두 독립변수(a,b) 간 교호작용이 없다.
H1: 두 독립변수 (a,b) 간 교호작용이 있다.
< 주효과 검정 >
각각의 독립변수가 종속변수에 미치는 영향을 주효과(main effect)라고 하며, 이를 검정하는 것을 주효과 검정이라 한다.
H0: a변수에 따른 종속변수 값에 차이가 없다.
H1: a변수에 따른 종속변수 값에 차이가 있다.
< 주효과 검정2 >
H0: b변수에 따른 종속변수 값에 차이가 없다.
H1: b변수에 따른 종속변수 값에 차이가 있다.
♡ 사후검정
분산분석 결과 귀무가설이 기각되어, 적어도 한 집단에서 평균의 차이가 있다는 것이 통계적으로 증명된 후, 어떤 집단들에 대해서 평균의 차이가 존재하는지 알아본다.
< 사후검정 종류 >
1) 튜키의 HSD
2) 던칸의 MRT
3) vltudml LSD
♡ ANOVA 파이썬 코드 예제
< 기본 설치 >
# conda
conda install -c conda-forge seaborn pandas statsmodels pingouin
# pip
pip install seaborn pandas statsmodels pingouin
# 시각화
pip install bioinfokit
# 기본라이브러리
import pandas as pd
import numpy as np
import statsmodels.formula.api import ols
import statsmodels.stats.anova import anova_lm
import seaborn as sns
import bioinfokit.analys import stat
*** 독립변수 간 교호작용 없도록 생성
1) 데이터생성하기
# 비료, 물 --> 높이
dataframe = pd.DataFrame({'Fertilizer': np.repeat(['daily', 'weekly'], 15),
'Watering': np.repeat(['daily', 'weekly'], 15),
'height': [14, 16, 15, 15, 16, 13, 12, 11, 14,
15, 16, 16, 17, 18, 14, 13, 14, 14,
14, 15, 16, 16, 17, 18, 14, 13, 14,
14, 14, 15]})
dataframe
sns.boxplot(x="Fertilizer", y="height", hue = "Watering", data = dataframe)
2) 가설설정
< 교호작용 검정 >
H0: 두 독립변수(Fertilizer,Watering) 간 교호작용이 없다.
H1: 두 독립변수 (Fertilizer,Watering) 간 교호작용이 있다.
< 주효과 검정 1>
H0: Fertilizer변수에 따른 종속변수 값에 차이가 없다.
H1: Fertilizer변수에 따른 종속변수 값에 차이가 있다.
< 주효과 검정2 >
H0: Watering변수에 따른 종속변수 값에 차이가 없다.
H1: Watering변수에 따른 종속변수 값에 차이가 있다.
3) Two way ANOVA test
import statsmodels.api as sm
from statsmodels.formula.api import ols
# two-way ANOVA
model = ols('height ~ C(Fertilizer) + C(Watering)+ C(Fertilizer):C(Watering)', data=dataframe).fit()
sm.stats.anova_lm(model, typ=2)
F-W interaction pvalue 0.91이다. 따라서 교호작용(상호작용)효과가 없다는 귀무가설을 채택한다.
Fertilizer pvalue, Watering의 pvalue는 0.9이다. 따라서 귀무가설을 채택하므로, 그룹 간 차이는 유의미하지 않다.
*** 독립변수 간 교호작용 있도록 생성
1) 데이터 가져오기
data = pd.read_csv("/kaggle/input/selling/Selling.csv")
data
sns.boxplot(x="Place", y= "Sales", hue= "Marketing", data = data)
2) 가설설정
H0: 두 독립변수(Place, Sales) 간 교호작용이 없다.
H1: 두 독립변수 (Place, Sales) 간 교호작용이 있다.
< 주효과 검정 1>
H0: Place변수에 따른 종속변수 값에 차이가 없다.
H1: Place변수에 따른 종속변수 값에 차이가 있다.
< 주효과 검정2 >
H0: Sales변수에 따른 종속변수 값에 차이가 없다.
H1: Sales변수에 따른 종속변수 값에 차이가 있다.
3) Two-way anova
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
formula = "Sales~ Marketing*Place"
two_anova = ols(formula, data= data).fit()
anova_lm(two_anova)
Marketing&Place pvalue가 0.05보다 작기 때문에 귀무가설을 기각한다. 즉, 두 독립변수 간에 교호작용이 있다.
4) interaction plot
interaction_plot(x, trace, response, colors, markers, ms)
☆ x : 하나의 독립변수
☆ trace : 다른 독립변수
☆ response : 종속변수
☆ colors, markers 색깔 , 마커
ms : 마커의 크기 marker size
교호작용 플롯은 독립변수와 종속변수의 관계가 두 번째 독립변수 요인의 값에 의해 어떻게 영향을 받는지 보여준다. 결과적으로 두 독립변수 간에 교호작용이 있는지 없는지 한 눈에 시각화할 수 있다.
fig = interaction_plot(data["Place"], data["Marketing"], data["Sales"], markers=['D', '^'], ms = 10)
두 독립변수 사이에 교호작용이 있기 때문에, 교차하는 선이 만들어졌다. 이것은 장소에 따라 다른 마케팅 전략이 판매량을 높인다는 점을 시사한다. 따라서 각각의 요인만 확인하는 것이 아니라, 두 요인의 결합이 더 중요하다는 인사이트를 얻을 수 있다.
5) 사후검정 테스트
pip install bioinfokit
from bioinfokit.analys import stat
result = stat()
result.tukey_hsd(df=data, res_var='Sales', xfac_var=['Place',"Marketing"], anova_model=formula)
result.tukey_summary
추가 정보 ++
https://olivia-blackcherry.tistory.com/599