2개의 모집단에 대한 평균을 비교, 분석하는 통계적 기법으로 t-Test를 활용하였다면, 비교하고자 하는 집단이 2개 이상일 경우에는 분산분석 (ANOVA : Analysis Of Variance)를 이용합니다. 

 

설명변수는 범주형 자료(categorical data)이어야 하며, 종속변수는 연속형 자료(continuous data) 일 때 2개 이상 집단 간 평균 비교분석에 분산분석(ANOVA) 을 사용하게 됩니다.

 

분산분석(ANOVA)은 기본적으로 분산의 개념을 이용하여 분석하는 방법으로서, 분산을 계산할 때처럼 편차의 각각의 제곱합을 해당 자유도로 나누어서 얻게 되는 값을 이용하여 수준평균들간의 차이가 존재하는 지를 판단하게 됩니다.  이론적인 부분에 대한 좀더 자세한 내용은 https://rfriend.tistory.com/131 를 참고하세요. 

 

one-way ANOVA  일원분산분석

 

이번 포스팅에서는 Python의  scipy 모듈의 stats.f_oneway() 메소드를 사용하여 샘플의 크기가 서로 다른 3개 그룹 간 평균에 차이가 존재하는지 여부를 일원분산분석(one-way ANOVA)으로 분석하는 방법을 소개하겠습니다. 

 

분산분석(Analysis Of Variance) 검정의 3가지 가정사항을 고려해서, 샘플 크기가 서로 다른 가상의 3개 그룹의 예제 데이터셋을 만들어보겠습니다. 

 

[ 분산분석  검정의 가정사항 (assumptions of ANOVA test) ]

  (1) 독립성: 각 샘플 데이터는 서로 독립이다. 
  (2) 정규성: 각 샘플 데이터는 정규분포를 따르는 모집단으로 부터 추출되었다. 
  (3) 등분산성: 그룹들의 모집단의 분산은 모두 동일하다. 

 

먼저, 아래의 예제 샘플 데이터셋은 그룹1과 그룹2의 평균은 '0'으로 같고, 그룹3의 평균은 '5'로서 나머지 두 그룹과 다르게 난수를 발생시켜 가상으로 만든 것입니다. 

 

# 3 groups of dataset with different sized samples 
import numpy as np
import pandas as pd
np.random.seed(1004)

data1 = np.random.normal(0, 1, 50)
data2 = np.random.normal(0, 1, 40)
data3 = np.random.normal(5, 1, 30) # different mean

data123 = [data1, data2, data3]


print(data123)
[Out]
[array([ 0.59440307,  0.40260871, -0.80516223,  0.1151257 , -0.75306522,
       -0.7841178 ,  1.46157577,  1.57607553, -0.17131776, -0.91448182,
        0.86013945,  0.35880192,  1.72965706, -0.49764822,  1.7618699 ,
        0.16901308, -1.08523701, -0.01065175,  1.11579838, -1.26497153,
       -1.02072516, -0.71342119,  0.57412224, -0.45455422, -1.15656742,
        1.29721355, -1.3833716 ,  0.3205909 , -0.59086187, -1.43420648,
        0.60998011,  0.51266756,  1.9965168 ,  1.42945668,  1.82880165,
       -1.40997132,  0.49433367,  0.9482873 , -0.35274099, -0.15359935,
       -1.18356064, -0.75440273, -0.85981073,  1.14256322, -2.21331694,
        0.90651805,  2.23629   ,  1.00743665,  1.30584548,  0.46669171]), array([-0.49206651, -0.08727244, -0.34919043, -1.11363541, -1.71982966,
       -0.14033817,  0.90928317, -0.60012686,  1.03906073, -0.03332287,
       -1.03424396,  0.15929405,  0.33053582,  0.02563551, -0.09213904,
       -0.91851177,  0.3099129 , -1.24211638, -0.33113027, -1.64086666,
       -0.27539834, -0.05489003,  1.50604364, -1.37756156, -1.25561652,
        0.16120867, -0.42121705,  0.2341905 , -1.20155195,  1.48131392,
        0.29105321,  0.4022031 , -0.41466037,  1.00502917,  1.45376705,
       -0.07038153,  0.52897801, -2.37895295, -0.75054747,  1.10641762]), array([5.91098191, 4.14583073, 4.71456131, 3.88528941, 5.23020779,
       5.12814125, 3.44610618, 5.36308351, 4.69463298, 7.49521024,
       5.41246681, 3.8724271 , 4.60265531, 4.60082925, 4.9074518 ,
       3.8141367 , 6.4457503 , 4.27553455, 3.63173152, 6.25540542,
       3.77536981, 7.19435668, 6.25339789, 6.43469547, 4.27431061,
       5.16694916, 7.21065725, 5.68274021, 4.81732021, 3.81650656])]

 

 

 

위의 3개 그룹의 커널밀도추정 그래프 (Kernel Density Estimates Plot)를 겹쳐서 그려보면, 아래와 같이 2개 집단은 서로 평균이 비슷하고 나머지 1개 집단은 평균이 확연히 다르다는 것을 직관적으로 알 수 있습니다. 

# Kernel Density Estimate Plot
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['figure.figsize'] = [10, 6]

sns.kdeplot(data1)
sns.kdeplot(data2)
sns.kdeplot(data3)
plt.show()

 

 

상자 그래프 (Box Plot) 으로 3개 집단 간 평균의 위치와 퍼짐 정도를 비교해보면, 역시 아래와 같이 그룹1과 그룹2는 서로 중심위치가 서로 비슷하고 그룹3만 중심위치가 확연히 다름을 알 수 있습니다. 

 

# Boxplot
sns.boxplot(data=data123)
plt.xlabel("Group", fontsize=14)
plt.ylabel("Value", fontsize=14)
plt.show()

 

 

이제 scipy 모듈의 scipy.stats.f_oneway() 메소드를 사용해서 서로 다른 개수의 샘플을 가진 3개 집단에 대해 평균의 차이가 존재하는지 여부를 일원분산분석을 통해 검정을 해보겠습니다. 

 

  - 귀무가설(H0): 3 집단의 평균은 모두 같다. (mu1 = mu2 = m3)

  - 대립가설(H1):  3 집단의 평균은 같지 않다.(적어도 1개 집단의 평균은 같지 않다) (Not H0)

 

F통계량은 매우 큰 값이고 p-value가 매우 작은 값이 나왔으므로 유의수준 5% 하에서 귀무가설을 기각하고 대립가설을 채택합니다. 즉, 3개 집단 간 평균의 차이가 존재한다고 평가할 수 있습니다

# ANOVA with different sized samples using scipy
from scipy import stats

stats.f_oneway(data1, data2, data3)
[Out] 
F_onewayResult(statistic=262.7129127080777, pvalue=5.385523527223916e-44)

 

 

F통계량과 p-value 를 각각 따로 따로 반환받을 수도 있습니다. 

# returning f-statistic and p-value
f_val, p_val = stats.f_oneway(*data123)

print('f-statistic:', f_val)
print('p-vale:', p_val)
[Out]
f-statistic: 262.7129127080777
p-vale: 5.385523527223916e-44

 

 

scipy 모듈의  stats.f_oneway() 메소드를 사용할 때 만약 데이터에 결측값(NAN)이 포함되어 있으면  'NAN'을 반환합니다. 위에서 만들었던 'data1'  numpy array 의 첫번째 값을  np.nan 으로 대체한 후에 scipy.stats.f_oneway() 로 일원분산분석 검정을 해보면  'NAN'(Not A Number)을 반환한 것을 볼 수 있습니다. 

 

# if there is 'NAN', then returns 'NAN'
data1[0] = np.nan
print(data1)
[Out]
array([        nan,  0.40260871, -0.80516223,  0.1151257 , -0.75306522,
       -0.7841178 ,  1.46157577,  1.57607553, -0.17131776, -0.91448182,
        0.86013945,  0.35880192,  1.72965706, -0.49764822,  1.7618699 ,
        0.16901308, -1.08523701, -0.01065175,  1.11579838, -1.26497153,
       -1.02072516, -0.71342119,  0.57412224, -0.45455422, -1.15656742,
        1.29721355, -1.3833716 ,  0.3205909 , -0.59086187, -1.43420648,
        0.60998011,  0.51266756,  1.9965168 ,  1.42945668,  1.82880165,
       -1.40997132,  0.49433367,  0.9482873 , -0.35274099, -0.15359935,
       -1.18356064, -0.75440273, -0.85981073,  1.14256322, -2.21331694,
        0.90651805,  2.23629   ,  1.00743665,  1.30584548,  0.46669171])

# returns 'nan'
stats.f_oneway(data1, data2, data3)
[Out] 
F_onewayResult(statistic=nan, pvalue=nan)

 

 

샘플 데이터에 결측값이 포함되어 있는 경우, 결측값을 먼저 제거해주고 일원분산분석 검정을 실시해주시기 바랍니다. 

 

# get rid of the missing values before applying ANOVA test
stats.f_oneway(data1[~np.isnan(data1)], data2, data3)
[Out]
F_onewayResult(statistic=260.766426640122, pvalue=1.1951277551195217e-43)

 

 

위의  일원분산분석(one-way ANOVA) 는 2개 이상의 그룹 간 평균의 차이가 존재하는지만을 검정할 뿐이며, 집단 간 평균의 차이가 존재한다는 대립가설을 채택하게 된 경우 어느 그룹 간 차이가 존재하는지는 사후검정 다중비교(post-hoc pair-wise multiple comparisons)를 통해서 알 수 있습니다. (rfriend.tistory.com/133)

 

pandas DataFrame 데이터셋에서 여러개의 숫자형 변수에 대해 for loop 순환문을 사용하여 집단 간 평균 차이 여부를 검정하는 방법은 rfriend.tistory.com/639 포스팅을 참고하세요. 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. 

 

[reference]

* scipy.stats.f_oneway : https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.f_oneway.html

 

scipy.stats.f_oneway — SciPy v1.6.3 Reference Guide

G.W. Heiman, “Understanding research methods and statistics: An integrated introduction for psychology”, Houghton, Mifflin and Company, 2001.

docs.scipy.org

728x90
반응형
Posted by Rfriend
,

2개의 모집단에 대한 평균을 비교, 분석하는 통계적 기법으로 t-Test를 활용하였다면, 비교하고자 하는 집단이 3개 이상일 경우에는 분산분석 (ANOVA : Analysis Of Variance)를 이용합니다. 

 

설명변수는 범주형 자료(categorical data)이어야 하며, 종속변수는 연속형 자료(continuous data) 일 때 3개 이상 집단 간 평균 비교분석에 분산분석(ANOVA) 을 사용하게 됩니다.

 

분산분석(ANOVA)은 기본적으로 분산의 개념을 이용하여 분석하는 방법으로서, 분산을 계산할 때처럼 편차의 각각의 제곱합을 해당 자유도로 나누어서 얻게 되는 값을 이용하여 수준평균들간의 차이가 존재하는 지를 판단하게 됩니다.  

 

 

 

[ 일원분산분석 (one-way ANOVA)의 개념 ]

 

 

 

 

분산분석 (ANOVA)은 영국의 통계학자 피셔(R.A. Fisher)가 고안한 분석 기법으로서, 최초에는 농사실험과 관련된 연구에서 사용되었으나 요즘에는 사회과학, 공학, 의학 등 폭넓게 적용되고 있습니다.

 

 

[ 분산분석 (ANOVA)의 분류 ]

 

 

 

3개 이상의 집단에 대한 비교 시 모집단이 정규분포를 따르지 않을 경우에는 비모수 추정과 검정 기법을 이용해야 하며, 이에 대해서는 추후에 별도로 다루도록 하겠습니다.

 

 

분산분석은 측정하고자 하는 값에 영향을 미치는 요인(Factor)의 수에 따라서 구분하는데요, 가령, 작업자(Worker)에 따라서 생산량(Volume of Production)의 차이가 있는가를 비교 분석한다고 했을 때 '작업자(Worker)'가 바로 요인(Factor)이 되겠습니다. 그리고 작업자 3명 '홍길동', '김철희', '최영희'를 요인 수준 (Factor Level) 혹은 처리 (treatment) 라고 합니다.

 

측정값에 영향을 미치는 요인(Factor)이 1 개인 실험은 '일원분산분석' (one-way ANOVA) 라고 하며, 측정값에 영향을 미치는 요인(Factor)이 2 개인 실험은 '이원분산분석' (two-way ANOVA) 라고 부릅니다.

 

 

[ ANOVA Model and Description in R ]

 

n-way ANOVA

Model

Description

one-way ANOVA

y ~ x1

  y is explained by x1 only

two-way ANOVA

y ~ x1 + x2

  y is explained by x1 and x2

two-way ANOVA

y ~ x1 * x2

  y is explained by x1, x2 and the

interaction   
  between them

three-way ANOVA

y ~ x1 + x2 + x3

  y is explained by x1, x2 and x3

 

 

 

이번 포스팅에서는 요인(Factor)이 1개인 일원분산분석(one-way ANOVA)의 이론과 R의 aov() 함수의 사용법에 대해서 설명해보겠습니다.

 

요인(Factor)이 1개이면서 r개의 요인 수준(Factor Level)을 가지고 있고, 각 수준에서 박복 측정수가 n개인 일원분산분석(one-way ANOVA)는 다음과 같은 형태의 자료 형태와 모형을 가지게 됩니다.

 

 

 

[ 일원분산분석 데이터 형태 (dataset for one-way ANOVA) ]

 

 

 

 

[ 일원분산분석 모형 (one-way ANOVA model) ] 

 

one-way ANOVA model

 

분산분석(ANOVA)은 측정된 자료값들의 전체 변동을 비교하고자 하는 요인 수준(Factor Level) 간의 차이에 의해서 발생하는 변동과 그 밖의 요인에 의해서 발생하는 변동으로 나누어서 자료를 분석하는 것이 기본 원리가 되겠습니다.

 

 

[ 분산분석 기본 원리 1 ]

 

 

 

위에서 제시한 식의 양변을 제곱하여 전체 측정값들에 대해서 모두 더하여 식을 정리하면 아래와 같이 됩니다. (자세한 과정은 생략함)

 

 

[ 분산분석 기본 원리 2 ]

 

 

 

 

위의 총제곱합(SST)와 처리제곱합(SSTR), 오차제곱합(SSE) 간의 기본원리를 이용하여 분산분석에서 사용하는 통계량들을 표로 일목요연하게 정리한 것이 아래의 일원분산분석표(one-way ANOVA table) 가 되겠습니다.

 

 

[ 원분산분석표 (one-way ANOVA table) ]

 

 구분

 제곱합

(Sum of Squares)

자유도

(degrees of freedom) 

평균제곱

(Mean Square Error) 

검정통계량 F0

(F statistics)

 처리

(Treatment)

 SSTR

r-1 

MSTR 

MSTR/MSE 

 오차

(Error)

 SSE

 nT - r

 MSE

 

 전체

(Total)

 SST

 nT - 1

 

 

 

 

분산분석표의 제일 오른쪽에 있는 F0 통계량을 이용해서 전체 수준들간의 평균이 같은지 아니면 다른지를 검정합니다.  기본 개념을 설명하자면, F0 통계량은 처리평균제곱 (MSTR)의 크기에 영향을 받으므로 처리평균제곱 (MSTR)이 커지면 오차평균제곱(MSE)은 작아지게 되며, 따라서 F0 통계량은 분자가 커지고 분모가 작아지므로 당연히 커지게 됩니다. 즉, F0 통계량 값이 크다는 것은 수준 평균들간에 평균의 차이가 크다는 의미가 되겠습니다.

 

 

그러면, 요인효과에 대한 검정을 위해서 분산분석에서는 아래와 같은 귀무가설과 대립가설을 사용하며, 검정통계량으로는 F 를 사용하여 기각역 또는 P-value 에 의해서 검정을 하게 됩니다.  

 

 

  [ 가설 ]

   귀무가설 H0 : μ1 = μ2 = ... = μr

   대립가설 H1 : 모든 μi 는 같지 않다.  i = 1, 2, ..., r

 

  [ 기각역 혹은 P-value에 의한 검정 ]

   표본으로부터 계산된 검정통계량의 값 f0가 유의수준(significance level) α 에서

 

   f0 >= Fα(r - 1, nT - r) 또는 P-value = P(F >= f0) < α 이면, H0 기각 (H0 reject)

 

   f0 < Fα(r - 1, nT - r) 또는 P-value = P(F >= f0) >= α 이면, H0 채택 (H0 accept)

 

 

 

 

이론 설명이 무척 길어졌는데요, 이제 드디어, 아래 문제를 R의 aov() 함수를 사용해서 풀어보도록 하겠습니다.

 

 

 


 

 

문제 ) 정유사에서 온도(Factor, one-way)에 따라서 휘발유 생산량에 변화가 있는지 (즉, 영향이 있는지) 알아보기 위하여 온도를 3가지 조건(3 Factor Levels)으로 실험설계를 하여 10번에 걸쳐서 휘발유 생산량을 측정하였습니다. 관찰값이 아래와 같을 때 조사되었을 때 온도의 조건에 따라서 휘발유 생산량에 차이가 있는지 유의수준 α = 10% 로 검정하시오.

 

 

 

 10번 실험에 의한 측정값

 요인 수준

(Factor Level)

 1

2

 10

 온도 조건 1

(Temp condition 1)

 50.5

52.1

51.9

52.4 

50.6

51.4 

51.2 

52.2 

51.5 

50.8 

 온도 조건 2

(Temp condition 2)

 47.5

47.7 

46.6 

47.1 

47.2 

47.8 

45.2 

47.4 

45.0 

47.9 

 온도 조건 3

(Temp condition 3)

 46.0

47.1

45.6

47.1

47.2

46.4

45.9 

47.1

44.9

46.2 

 


 

R에 (1) 위의 관측값들을 벡터로 입력하고,

      (2) boxplot() 함수와 summary()를 이용하여 탐색적 분석을 해본 후에,

      (3) aov() 함수를 이용하여 one-way ANOVA 분석을 하였으며 
         : aov(y ~ group, data = dataset)

      (4) 기본 가정 중에 오차의 등분산성을 검정하기 위해 Bartlett 검정

         : bartlett.test(y ~ group, data = dataset)

을 실시하였습니다.

 

 

이때 조심해야 할 것이 있는데요, dataset을 데이터프레임으로 해서 그룹(group, factor level)에 해당하는 변수는 반드시 요인(Factor)형이어야 하므로, 만약 요인(Factor)형이 아니라면 먼저 transfrom(factor()) 함수를 이용해서 변환을 시켜주고 ANOVA 분석을 수행해야 합니다.

 

 

> ##---------------------------------------------------------- 
> ## One-way ANOVA : aov(), oneway.test 
> ##---------------------------------------------------------- 
>  
> ##--- Are there any daily outcome differences among temperature conditions? 
> # group 1 : temperature condition 1  
> # group 2 : temperature condition 2 
> # group 3 : temperature condition 3 
>  
> # daily outcome by tmep condition (group 1/2/3) 
> y1 <- c(50.5, 52.1, 51.9, 52.4, 50.6, 51.4, 51.2, 52.2, 51.5, 50.8) 
> y2 <- c(47.5, 47.7, 46.6, 47.1, 47.2, 47.8, 45.2, 47.4, 45.0, 47.9) 
> y3 <- c(46.0, 47.1, 45.6, 47.1, 47.2, 46.4, 45.9, 47.1, 44.9, 46.2) 
>  
> y <- c(y1, y2, y3) 
> y  
[1] 50.5 52.1 51.9 52.4 50.6 51.4 51.2 52.2 51.5 50.8 47.5 47.7 46.6 47.1 47.2 47.8 45.2 47.4 45.0 
[20] 47.9 46.0 47.1 45.6 47.1 47.2 46.4 45.9 47.1 44.9 46.2 
>  
> n <- rep(10, 3) > n [1] 10 10 10 
>  
> group <- rep(1:3, n) 
> group  [1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 
>  
> # combining into data.frame 
> group_df <- data.frame(y, group) 
> group_df       
y group 
1  1 50.5     1 2  52.1     1 3  51.9     1 4  52.4     1 5  50.6     1 6  51.4     1 7  51.2     1 8  52.2     1 9  51.5     1 10 50.8     1 11 47.5     
2 12 47.7     2 13 46.6     2 14 47.1     2 15 47.2     2 16 47.8     2 17 45.2     2 18 47.4     2 19 45.0     2 20 47.9     2 21 46.0     
3 22 47.1     3 23 45.6     3 24 47.1     3 25 47.2     3 26 46.4     3 27 45.9     3 28 47.1     3 29 44.9     3 30 46.2     3 
>  
> sapply(group_df, class)         
y     group  
"numeric" "integer"  
>  
> # transform from 'integer' to 'factor' 
> group_df <- transform(group_df, group = factor(group)) 
> sapply(group_df, class)         
y     group  
"numeric"  "factor"  
>  
>  
> # boxplot 
> attach(group_df) 
The following objects are masked _by_ .GlobalEnv:      group, y  
> boxplot(y ~ group,  
+         main = "Boxplot of Daily Outcome by Temperature condition 1/2/3",  
+         xlab = "Factor Levels : Temperature condition 1/2/3",  
+         ylab = "Daily Outcome") 
>  

 

 

 

>  
> # descriptive statistics by group 
> tapply(y, group, summary) 
$`1`    
Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    
50.50   50.90   51.45   51.46   52.05   52.40   
$`2`    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    
45.00   46.72   47.30   46.94   47.65   47.90   
$`3`    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    
44.90   45.92   46.30   46.35   47.10   47.20   
>
>
> detach(group_df) 
>  
> # one-wayANOVA 
> aov(y ~ group, data = group_df) 
Call:    aov(formula = y ~ group, data = group_df)  
Terms:                   
group Residuals Sum of Squares  156.302    19.393 
Deg. of Freedom       2        27  
Residual standard error: 0.8475018 Estimated effects may be unbalanced 

 

> summary(aov(y ~ group, data = group_df))             
Df Sum Sq Mean Sq F value  Pr(>F)     
group        2 156.30   78.15   108.8 1.2e-13 *** Residuals   27  19.39    0.72                     
--- Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 

일원분산분석(one-way ANOVA) 결과 검정통계량 F-value가 108.8으로 나왔으며, P-value 값은 '1.2e-13'으로서 매우 작게 나와 유의수준 10% 에서 귀무가설을 기각하고 대립가설을 채택하게 되어 "온도 조건 1/2/3에 따라서 휘발유 생산량에 차이가 있다"라고 판단할 수 있겠습니다.

 

 오차의 등분산성 가정에 대해 Bartlett 검정 결과 P-value가 0.4368로서 유의수준 10%에서 귀무가설을 채택하여 "오차의 등분산성 가정을 만족한다"고 할 수 있겠습니다.

 

많은 도움 되었기를 바랍니다.

 

질문은 댓글로 남겨주세요.

 

  • 쌍을 이룬 집단 간 평균 다중비교 (multiple comparisons)

Tukey's HSD(honestly significant difference) test 참조

Duncan's LSR(least significant range) test 참고

 

 

  • 대비 (contrast)

샤페 검정법 (scheffe test) 참고

 

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요. ^^

 

728x90
반응형
Posted by Rfriend
,