Kaggle

7차시 분산분석 ANOVA 총정리 one-way, two-way, 일원배치, 이원배치, anova, 사후검정, TukeyHSD, interaction_plot(), 교호작용, 포아송분포

Olivia-BlackCherry 2024. 4. 10. 14:38

목차

    분산분석

    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값이 높으므로 귀무가설을 기각하지 않는다.

    즉, 모든 그룹의 평균에 차이가 없으니 인종별 유권자 나이 평균은 모두 같다.

    sm.stats.anova_lm()
    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) 데이터 가져오기

    Selling.csv
    0.00MB

     

    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/626#4._%EC%9D%B4%EC%9B%90%EB%B0%B0%EC%B9%98_%EB%B6%84%EC%82%B0%EB%B6%84%EC%84%9D_Two-way_ANOVA

     

    [ADP 데이터분석 전문가- 통계편] 분산분석, ANOVA, 일원배치, 이원배치, two-way ANOVA, Kruskal, welch ANOVA,

    목차 1. 분산분석이란? 분산분석은 다수( 2,3,4개 ..) 집단 간 평균을 비교한다. 독립변수 : 범주형 종속변수 : 연속형 2. 종류 영향을 주는 독립변수의 개수에 따라 1 ANOVA 일원배치 분산분석 2 Two-way

    olivia-blackcherry.tistory.com

     

     

    https://olivia-blackcherry.tistory.com/599

     

    ANOVA 개념, Python, hypothesis testing with python

    ANOVA(Analysis of Variance)는 그룹 간의 평균 차이를 비교하기 위해 사용되는 통계적인 방법입니다. 주로 세 개 이상의 그룹을 비교하는 경우 사용되며, 그룹 내의 변동과 그룹 간의 변동을 분석하여

    olivia-blackcherry.tistory.com

     

     

    https://olivia-blackcherry.tistory.com/598