본문 바로가기
Data Analysis/Statistics

Python_Statistics_Anova(분산분석)

by mansoorrr 2023. 12. 11.

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")

am별 분포 및 기초통계량 확인

 

  • 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")

cyl별 분포 및 기초통계량 확인

 

  • 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")

am, 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", "^"])

two-away anova 실시

 

  • 사후검정
  • 결과가 유의미하게 나타난 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