본문 바로가기

■ 프로그래밍/R

[R] Two Sample T-test

[ R : Two Sample T-test ]


# 주석

## 결과값


패키지 설치 및 로딩하기

install.packages("ggplot2")
install.packages("nortest")  # Anderson-Darling test: 정규성 검정
library(ggplot2)
library(nortest)


Two sample test

  • two sample test는 언제 하는가?
    A. 두 개의 독립적인 모집단의 평균이 다른지를 통계적으로 분석하는 방법

  • 자료: 양적자료 1개, 질적자료 1개(2개의 값으로 이루어져야 함)


방법

Two Sampel T-tset의 가설검정 단계는 아래와 같다.

1단계: 정규성 검정
2단계: 등분산 검정(Equality of Variance Test)
3단계: Two Sample T-test


예제데이터: ggplot2::diamonds

diamonds$cut.group <- ifelse(diamonds$cut == "Ideal",   # ifelse(조건, 
                             "Ideal",                   #        만족할 때의 결과 값,
                             "Non-Ideal")               #        만족 못할 때의 결과 값)

질적자료는 2개의 값으로 만들어줘야 하기 때문에, diamonds$cut(종류)를 Ideal과 Ideal이 아닌 것(Non-Ideal)로 나누겠다.


[ 가설 1 ]

  • 귀무가설: Ideal과 Non-Ideal 간에 price에는 차이가 없다(mu1 = mu2)
  • 대립가설: Ideal의 평균 price가 Non-Ideal의 평균 price보다 높다 (mu1 > mu2)


1단계: 각 집단에 대한 정규성 검정

  • 귀무가설: 정규분포를 따른다
  • 대립가설: 정규분포를 따르지 않는다


Anderson-Darling Test: 데이터가 5,000개 이상일 경우 shapiro test 대신 사용한다.

nortest::ad.test(data$variable)

양적자료 1개(Price)와 질적자료가 2개의 값(Ideal, Non-Ideal)으로 이루어져 있기 때문에, 각 자료가 정규분포를 따르는지 확인해 봐야 한다.

nortest::ad.test(diamonds[diamonds$cut.group == "Ideal", ]$price)
nortest::ad.test(diamonds[diamonds$cut.group != "Ideal", ]$price)

이렇게 두 개의 코드를 작성한 후 각각의 p-value를 확인하는 방법도 있지만,
앞의 by함수를 쓰면 한 번에 확인 가능하다.


by(diamonds$price, diamonds$cut.group, ad.test)
## diamonds$cut.group: Ideal
## 
##  Anderson-Darling normality test
## 
## data:  dd[x, ]
## A = 1865.1, p-value < 2.2e-16
## 
## -------------------------------------------------------- 
## diamonds$cut.group: Non-Ideal
## 
##  Anderson-Darling normality test
## 
## data:  dd[x, ]
## A = 1725.8, p-value < 2.2e-16

결론: 두 p-value가 모두 2.2e-16(0.000)이므로, 모두 정규성 가정이 깨짐


2단계: Wilcoxon’s rank sum test (윌콕슨의 순위 합 검정)

wilcox.test(diamonds$price ~ diamonds$cut.group,    # wilcox.test(양적자료 ~ 질적자료)
            alternative = "greater")                           
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  diamonds$price by diamonds$cut.group
## W = 300690000, p-value = 1
## alternative hypothesis: true location shift is greater than 0

결론: 유의확률이 1.000이므로, 유의수준 0.05에서 귀무가설 채택.
즉, Ideal과 Non-Ideal 간에 price간 통계적으로 유의한 차이는 없다.


만약 2단계에서 등분산 가정이 만족 되었다면,

3단계: 등분산이 가정된 독립 2표본 t검정(two sample t-test of equal variance)

t.test(diamonds$price ~ diamonds$cut.group,
       alternative = "greater",
       var.equal = TRUE)
## 
##  Two Sample t-test
## 
## data:  diamonds$price by diamonds$cut.group
## t = -22.676, df = 53938, p-value = 1
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
##  -848.8983       Inf
## sample estimates:
##     mean in group Ideal mean in group Non-Ideal 
##                3457.542                4249.027

결론: 유의확률이 1.000이므로 유의수준 0.05에서 귀무가설 채택.
즉, Ideal과 Non-Ideal간의 price에는 유의한 차이가 없다.


사실, 3가지 방법을 모두 써도 결과는 똑같이 Ideal과 Non-Ideal간의 Price에는 유의한 차이가 없다는 결과가 나온다.
그렇다면 그냥 아무 방법이나 써도 되지 않나?란 생각이 들텐데, 꼭 그렇지만은 않다.
데이터의 유형과 모양에 따라 결과가 귀무가설or대립가설로 확 나뉠 수 있다.
물론, 실전에서는 귀무가설을 채택해야 하지만, 경우에 따라 그냥 대립가설을 채택할 수도 있다.
상황은 언제든지 유연하게 바뀔 있으니 그에 따라 최적의 방법을 따르는 것이 가장 좋다.


[ 가설 2 ]

직접 위의 코드를 보면서 따라해보자.

귀무가설: Ideal과 Non-Ideal 간에 carat의 평균에 차이가 없다(mu1 = mu2)
대립가설: Ideal과 Non-Ideal 간의 carat의 평균에 차이가 있다(mu1 != mu2)


[ 실습 ]

for문을 이용해 Ideal과 Non-Ideal간의 carat, depth, table, price, x, y, z의 평균에 차이 있다를 검정해보자.

  • 귀무가설: Ideal과 Non-Ideal간에 carat, depth, table, price, x, y, z의 평균에 차이가 없다
  • 대립가설: Ideal과 Non-Ideal간에 carat, depth, table, price, x, y, z의 평균에 차이가 있다


직접 따라 쳐보는 것을 추천한다.

colnames(diamonds) # 양적, 질적 위치 파악하기 - 1, 5:10

T.method <- c()      # for문을 실행하고 나온 결과값을 저장하기 위해
T.equality <- c()    # 미리 비어있는 vector 만들기
T.statistic <- c()
T.pvalue <- c()


for(i in c(1, 5:10)){
  # 1단계
  norm.test <- by(unlist(diamonds[, i]), diamonds$cut.group, ad.test) 
  
  # 1단계가 만족할 경우
  if((norm.test$Ideal$p.value >= 0.05) & (norm.test$'Non-Ideal'$p.value >= 0.05)){
    # 2단계 검정
    var.test.result <- var.test(unlist(diamonds[ , i]) ~ diamonds$cut.group)        
    
    
    if(var.test.result$p.value >= 0.05){
      # 2단계가 만족할 경우
      t.test.result <- t.test(unlist(diamonds[ , i]) ~ diamonds$cut.group,
                                     alternative = "two.sided",
                                     var.equal = TRUE)
      
      T.method    <- c(T.method, t.test.result$method)
      T.equality  <- c(T.equality, "YES")
      T.statistic <- c(T.statistic, t.test.result$statistic)
      T.pvalue    <- c(T.pvalue, t.test.result$p.value)
    }else{
      # 2단계가 깨졌을 경우
      t.test.result <- t.test(unlist(diamonds[ , i]) ~ diamonds$cut.group,
                                     alternative = "two.sided",
                                     var.equal = FALSE)
      
      T.method    <- c(T.method, t.test.result$method)
      T.equality  <- c(T.equality, "NO")
      T.statistic <- c(T.statistic, t.test.result$statistic)
      T.pvalue    <- c(T.pvalue, t.test.result$p.value)
    }
    
  }else{
 # 1단계가 깨졌을 경우
    wilcoxon.test.result <- wilcox.test(unlist(diamonds[ , i]) ~ diamonds$cut.group,
                                               alternative = "two.sided")
    
    T.method    <- c(T.method, wilcoxon.test.result$method)
    T.equality  <- c(T.equality, "NULL")
    T.statistic <- c(T.statistic, wilcoxon.test.result$statistic)
    T.pvalue    <- c(T.pvalue, wilcoxon.test.result$p.value)
  }
}

# 데이터 저장하기
resultDF <- data.frame(Variable  = colnames(diamonds)[c(1, 5:10)],
                       Method    = T.method,
                       Equality  = T.equality,
                       Statistic = T.statistic,
                       PValue    = T.pvalue)
# 데이터 보기
View(resultDF)