Anova(분산분석)
- T-검정: 두 집단 사이의 평균 차이를 비교
- Anova: 두 개 이상의 다시 집단간 평균을 비교
- F검정 통계량 사용(F분포)
- 정규분포를 이루는 모집단에서 독립적으로 추출한 표본들의 분산비율이 나타내는 연속확률분포
- 두 집단간 분산의 동일성 검정에 사용
- 종속변수: 연속형
- 독립변수: 범주형
- 변수의 개수에 따라 일원, 이원, 다원배치 분산분석으로 나뉨
- 단일변량 분산분석
- 일원배치 분산분석: 독립변수 1개, 종속변수 1개
- 이원배치 분산분석: 독립변수 2개, 종속변수 1개
- 다원배치 분산분석: 독립변수 3개 이상, 종속변수 1개
- 다변량 분산분석
- MANOVA: 독립변수 1개이상, 종속변수 2개 이상
- 단일변량 분산분석
- 집단 간 평균 차이를 집단 내 변동에 비교하여 살펴보는 통계 방법
- 집단 내 분산 보다 집단 간 분산이 크다면 유의하다고 할 수 있음
- 평균이 같은 집단이 있더라도 분산이 다르면 그 평균은 같다고 할 수 없기 때
- 조건
- 집단의 측정치는 서로 독립적이다
- 집단의 측정치는 정규분포를 따른다
- 집단의 측청치는 분산이 같다(등분산)
- 후속검정
- 분산분석의 결과로는 집단간 평균이 차이가 있는지 없는지만 검증 가능하다
- 집단간 어떻게 차이가 있는지 설명하기 위해 후속검정(post-hoc)이 필요하다
- 던칸의 MRT, vltudml LSD, 튜키의 HSD방법이 있다.
One-way Anova(일원배치분산분석)
- 독립변수 1개, 종속변수 1개
- F검정 통계량 사용
- 결국 F-value는 분산의 비율값임
- F값이 크다는 것은 집단간 분산이 커지고 귀무가설을 기각할 확률이 높아짐
- 즉 집단 간의 차이가 있다고 할 확률이 높아짐
- 집단 내에서 우연히 발생할 수 있는 분산보다 집단 간의 분산이 F배 크다는 의미
요인 | 제곱합 | 자유도 | 평균제곱 | F-value | p-value |
집단 간 | SSB | 집단 수 - 1 | 집단간 제곱합 / 자유도 | 집단 간 평균제곱 / 집단 내 평균제곱 | - |
집단 내 | SSW | 자료수 - 집단수 | 집단 내 제곱합 / 자유도 | - | |
합계 | TSS | 자료수 - 1 | - | - |
- 가정
- 비교하고자 하는 샘플들이 독립적
- 모집단은 정규분포
- 모든 집단의 분산이 동일
- 가설
- H0: 집단간 모평균은 차이가 없다.
- H1: 집단간 모평균은 차이가 있다.
- 단계
- 정규성검정
- H0: 정규성을 가정한다 -> 등분산 검정 실시
- H1: 정규성을 가정하지 않는다 -> 크루스칼 왈리스 순위합 검정
- 등분산 검정
- H0: 집단간 분산에 차이가 없다(등분산이다) -> one-way Anova
- H1: 집단간 분산에 차이가 있다(이분산이다) -> welck_anova
- avova
- H0: 집단간 평균에 차이가 없다 -> 종료
- H1: 집단간 평균에 차이가 있다 -> 후속검정
- 정규성검정
예시 1) iris 데이터의 종별로 꽃받침의 폭(Sepal.Width)의 평균이 같은가?
- 데이터 준비
- 데이터는 4개의 수치형 변수와 1개의 명목형 변수로 이루어져 있다.
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
#데이터 로드
dataset = load_iris()
data = dataset["data"]
target = dataset["target"]
feature_names = dataset["feature_names"]
#데이터정의
iris = pd.DataFrame(data, columns=feature_names)
#target변수 추가
iris["target"] = target
iris.head()
#타겟변수 변환
for i in range(iris.shape[0]):
if iris.loc[i, "target"] == 0:
iris.loc[i, "target"] = dataset["target_names"][0]
elif iris.loc[i, "target"] == 1:
iris.loc[i, "target"] = dataset["target_names"][1]
elif iris.loc[i, "target"] == 2:
iris.loc[i, "target"] = dataset["target_names"][2]
#컬럼명 변경
iris.rename(columns={
"sepal length (cm)": "sepal_length",
"sepal width (cm)": "sppal_width",
"petal length (cm)": "petal_length",
"petal width (cm)": "petal_width"
}, inplace=True)
iris.head()
- 타겟별 분포 확인
- 타겟별 count가 동일하다
- target별로 기초통계량과 시각화 한 결과 평균의 차이가 있다.
#target별 sepalwidth 기초통계량 확인
iris.groupby("target")["sepal_width"].agg(["count", "min", "median", "mean", "max"])
#target별 sepalwidth boxplot
import matplotlib.pyplot as plt
import seaborn as sns
sns.boxplot(data=iris, x="target", y="sepal_width")
- 실제로 타겟별 평균의 차이가 있는지 확인하기 위해 가설을 수립한다.
- H0: target별 평균의 차이가 없다.
- H1: target중 적어도 하나는 평균의 차이가 있다.
- 가설을 수립했으니 분산분석의 순서에 따라 정규성검정을 먼저 실시한다.
- H0: 정규성을 가정한다
- H1: 정규성이 가정되지 않는다
- setosa, versicolor, virginica 모두 p-value > .05 이므로 귀무가설을 채택한다.
- 정규성을 가정하기 때문에 등분산 검정으로 넘어간다
- 만약 집단 하나라도 정규성이 만족되지 않는다면 kruskal검정을 고려해봐야 한다.
#----------target별 정규성 검정
import scipy.stats as stats
#타겟별 sepal_width분류
setosa = iris.loc[iris["target"]=="setosa", "sepal_width"]
versicolor = iris.loc[iris["target"]=="versicolor", "sepal_width"]
virginica = iris.loc[iris["target"]=="virginica", "sepal_width"]
#정규성 검정
print(f"setosa shapiro test: {stats.shapiro(setosa)}")
print(f"versicolor shapiro test: {stats.shapiro(versicolor)}")
print(f"virginica shapiro test: {stats.shapiro(virginica)}")
#결과
setosa shapiro test: ShapiroResult(statistic=0.971718966960907, pvalue=0.2715126574039459)
versicolor shapiro test: ShapiroResult(statistic=0.9741329550743103, pvalue=0.3379843533039093)
virginica shapiro test: ShapiroResult(statistic=0.9673907160758972, pvalue=0.18089871108531952)
- 정규성 검정결과 세 집단 모두 정규성을 가정한다.
- 따라서 등분산검정을 실시한다.
- H0: 집단별 분산은 차이가 없다(등분산)
- H1: 집단별 분산은 차이가 있다(이분산)
- 등분산검정 결과 p-value >.05로 나타나 H0를 채택한다.
#등분산 검정 실시
stats.levene(setosa, versicolor, virginica)
#결과
LeveneResult(statistic=0.5902115655853319, pvalue=0.5555178984739075)
- 등분산검정 결과 등분산이 가정되었다.
- 따라서 anova 검정을 실시한다.
- F=49.16, p-value < .05로 나타나 처음 세운 가설인 H0를 기각한다.
- 따라서 집단 간 평균은 차이가 있다.
#----------one-way anova 실시
stats.f_oneway(setosa, versicolor, virginica)
#결과
F_onewayResult(statistic=49.160040089612075, pvalue=4.492017133309115e-17)
- 집단 간 차이가 있으므로 사후검정을 실시한다.
- 사후검정으로는 tukey의 hsd검정을 사용한다.
- 사후검정의 가설은 다음과 같다.
- H0: 집단들 사이에 평균은 같다.
- H1: 집단들 사이에 평균은 같지 않다
- 사후검정을 실시하면 집단 간 짝지을수 있는 모든 경우의 수를 비교한다.
- 사후검정 결과 모든 경우의수에서 p-adj < .05 이므로 귀무가설을 기각한다.
- 따라서 집단들 사이에 평균은 같지 않다고 할 수 있다.
- meandiff는 group2 - group1 의 결과이다.
- 예를들어 group2(versicolor) - group1(setosa) = -0.658 이라면
- group1(setosa)의 평균이 크다 라는 것을 의미한다.
#---------- 사후검정
from statsmodels.stats.multicomp import pairwise_tukeyhsd
from statsmodels.stats.multicomp import MultiComparison
#사후검정 실시
mc = MultiComparison(
data = iris["sepal_width"],
groups = iris["target"]
)
tukey = mc.tukeyhsd(alpha=.05)
print(tukey.summary())
#사후검정 시각화
fig = tukey.plot_simultaneous()
- 정규성이 가정되지 않았을 경우: kruskal검정 실시
#----------- 정규성을 가정하지 않았을 경우
stats.kruskal(setosa, versicolor, virginica)
#결과
#p-value<.05 이므로 H0를 기각한다. 집단간 평균 차이가 있음
KruskalResult(statistic=63.571146104163795, pvalue=1.5692820940316782e-14)
- 등분산이 가정되지 않았을 경우: pg.welch_anova(data=data, dv="종속변수", between="독립변수")
#----------- 등분산이 가정되지 않았을 경우
#패키지 설치
pip install pingouin
#검정 실행
import pingouin as pg
pg.welch_anova(data=iris, dv="sepal_width", between="target")
#결과
p-value<.05 이므로 H0를 기각한다. 집단 간 차이가 있다.
Two-way Anova(이원배치분산분석)
- 하나의 종속변수(연속형)에 대한 두개의 독립변수(범주형)의 영향을 알아보기 위해 사용되는 검증방법
- ex) 성별과 학년에 따른 시험점수의 차이
- 종속변수에 대한 두 개의 독립변수의 상호 관련성을 평가하기 위함
- 두 독립변수 사이에 상관관계에 대한 검증이 반드시 진행되어야 함
- F검정 통계량 사용
요인 | 제곱합 | 자유도 | 평균제곱 | F-value |
요인A | SSA | I - 1 | ms(00) = 제곱합/자유도 | MS(A) / MSE |
요인B | SSB | J - 1 | MS(B) / MSE | |
상호작용 | SS(A*B) | (I - 1)(J - 1) | MS(AB) / MSE | |
오차 | SSE | IJ(n - 1) | MSE = SSE / IJ(n-1) | - |
전체 | SST | IJn - 1 | - | - |
- 가정
- 집단의 측정치는 서로 독립
- 집단은 정규분포를 따름
- 집단 측정치의 분산이 동일(등분산)
- 가설: 이원분산분석의 가설은 6가지
가설 | 가설종류 | 가설내용 |
주효과검정1 | H0 | H0: A집단에 따른 종속변수값은 차이가 없다. |
H1 | A집단에 따른 종속변수값은 차이가 있다. | |
주효과검정2 | H0 | B집단에 따른 종속변수값은 차이가 없다. |
H1 | B집단에 따른 종속변수값은 차이가 없다. | |
교호작용 검정 | H0 | A와 B변수의 상호작용효과가 없다. |
H1 | A와 B변수의 상호작용효과가 있다. |
- 단계는 One-way anova와 동일
- 독립변수의 level이 2개이므로 검정과 해석에 집중해야 함
예시 1) mtcars데이터에서 변속기종류와 실린더 갯수에 따라 주행거리 평균에 유의미한 차이가 존재하는지 확인
- 데이터 로드 및 필요 변수 추출
- shape: (32, 3)
- 데이터 타입 및 변수정보 확인
- am(변속기종류): 현재 int형이지만 category형(기초통계량 확인시 0과 1로 이루어짐)
- cyl(실린더갯수): 현재 int형이지만 category형(기초통계량 확인시 4, 6, 8로 이루어짐)
- mpg(주행거리: float
- 결측치 갯수 확인: 0개
#분석에 필요한 컬럼만 추출(변속기종류, 실린더 갯수)
mtcars = mtcars[["am", "cyl", "mpg"]]
print(mtcars.shape)
mtcars.head()
#정보확인 및 결측치 갯수 확인
mtcars.info()
#기초통계량확인
mtcars.describe()
- am(변속기종류)별 주행거리 통계량 확인
- 1번 변속기가 0번 변속기에 비해 평균 주행거리가 길다.
- 1번 변속기가 최소, 최대 주행거리모두 길다
- 0번 변속기는 정규분포 모양, 1번 변속기는 분포가 들쑥날쑥
#am별 평균 주행거리
mtcars.groupby("am")["mpg"].agg(["count", "min", "median", "mean", "max"])
#am별 평균주행거리 시각화
grid = sns.FacetGrid(data=mtcars, col="am")
grid.map(sns.histplot, "mpg")
sns.boxplot(data=mtcars, x="am", y="mpg")
- cyl(실린더)별 주행거리 통계량 확인
- 실린더 갯수는 4, 6, 8개로 나뉨
- 4의 평균 주행거리가 가장 높고 6, 8 순으로 나타남
- 4는 주행거리가 20에서 35까지 분포
- 8은 주행거리가 10에서 20까지 분포
- 6은 주행거리가 17에서 22까지 좁게 분포
#cyl별 주행거리 확인
cyl_mpg = mtcars.groupby("cyl")["mpg"].agg(["count", "min", "median", "mean", "max"])
cyl_mpg
#cyl별 주행거리 시각화
grid = sns.FacetGrid(data=mtcars, col="cyl")
grid.map(sns.histplot, "mpg")
sns.boxplot(data=mtcars, x="cyl", y="mpg")
- am, cyl별 주행거리 및 통계량 확인
- 0번, 1번 변속기 모두 평균주행거리는 4번 실린더>6번 실린더>8번 실린더순으로 나타남
- 0번 변속기에는 8번 실린더를 가진 차량 수가 많지만 1번 변속기는 4 실린더를 장착한 차량 수가 많다.
- 0번 변속기에 4번 실린더를 장착했을때 평균 주행거리와 1번 변속기에 4번 실린더를 장착했을때 평균과 최대 주행거리가 차이가 크다. 6번과 8번은 평균에서는 큰 차이가 없다.
#am, cyl별 통계량
am_cyl_mpg = mtcars.groupby(["am", "cyl"])["mpg"].agg(["count", "min", "median", "mean", "max"])
am_cyl_mpg
#시각화(histogram)
grid = sns.FacetGrid(data=mtcars, hue="cyl", col="am")
grid.map(sns.histplot, "mpg")
plt.legend()
#시각화(boxplot)
sns.boxplot(data=mtcars, x="am", y="mpg", hue="cyl")
- 가설설정
감정 | 가설종류 | 내용 |
주효과검정 1 | H0 | 변속기 종류(am)에 따른 주행거리는 차이가 없다. |
H1 | 변속기 종류(am)에 따른 주행거리는 차이가 있다. | |
주효과검정 2 | H0 | 실린더 종류(cyl)에 따른 주행거리는 차이가 없다. |
H1 | 실린더 종류(cyl)에 따른 주행거리는 차이가 있다. | |
교호작용 검정 | H0 | 변속기종류와 실린더 갯수 간에는 상호작용 효과가 없다. |
H1 | 변속기 종류와 실린더 갯수 간에는 상호작용 효과가 있다. |
- anova 실시
- cyl:am 의 p-value > .05 이므로 교호작용 검정의 귀무가설을 기각하지 않는다. 따라서 변속기 종류와 실린더 갯수 간에는 상호작용 효과가 없다.
- 교호작용이 존재하지 않을때 주효과 검정이 의미를 갖는다.
- cyl의 p-value < .05 이므로 주효과검정2의 귀무가설을 기각한다. 따라서 실린더 종류에 따른 주행거리는 차이가 있다.
- am의 p-value >.05 이므로 주효과검정 1의 귀무가설을 기각하지 않는다. 따라서 변속기 종류에 따른 주행거리는 차이가 없다.
- 교호작용 검정 결과 교호작용이 없다는 것을 확인하였다. 이를 그래프로 표현하면 아래 그림과 같다.
- 일반적으로 상호작용 그래프에서 서로 교차하고 있을 시 독립변수간에 상호작용이 존재한다고 볼 수 있다.
#---------- two-way anova 실시
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
#식 작성
formula = "mpg ~ C(am) + C(cyl) + C(am):C(cyl)"
model = ols(formula, mtcars).fit() #모델생성
aov_table = anova_lm(model, typ=2) #결과확인
aov_table
#---------- 교호작용 시각화
from statsmodels.graphics.factorplots import interaction_plot
import matplotlib.pyplot as plt
#interaction_plot(x1, x2, y) 순으로 입력
fig = interaction_plot(mtcars["cyl"], mtcars["am"], mtcars["mpg"], markers=["D", "^"])
- 사후검정
- 결과가 유의미하게 나타난 cyl변수에 대해서 사후검정을 실시한다.
- 사후검정 결과 모든 그룹에서 p-adj < .05로 유의미하게 나타난다.
- 따라서 실린더 수(cyl)에 따른 평균 주행거리는 4 > 6 > 8로 나타난다.
#---------- 사후검정 실시
from statsmodels.stats.multicomp import pairwise_tukeyhsd
from statsmodels.stats.multicomp import MultiComparison
mc = MultiComparison(
data = mtcars["mpg"],
groups = mtcars["cyl"]
)
tukey = mc.tukeyhsd(alpha=.05)
print(tukey.summary())
'Data Analysis > Statistics' 카테고리의 다른 글
Python_Statistics_Regression (0) | 2024.03.01 |
---|---|
Python_Statistics_Chi-square (0) | 2023.12.15 |
Python_Statistics_t-test (1) | 2023.12.07 |
R-연관분석 (0) | 2023.12.03 |
R기초 (1) | 2023.12.03 |