setting

library(ggplot2)
## Warning: package 'ggplot2' was built under R version 3.6.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 3.6.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(lmPerm)
## Warning: package 'lmPerm' was built under R version 3.6.3
PSDS_PATH <- file.path('~', 'statistics-for-data-scientists')

session_times <- read.csv(file.path(PSDS_PATH, 'data', 'web_page_data.csv'))
session_times[,2] <- session_times[,2] * 100
four_sessions  <- read.csv(file.path(PSDS_PATH, 'data', 'four_sessions.csv'))
click_rate <-  read.csv(file.path(PSDS_PATH, 'data', 'click_rates.csv'))
imanishi <-  read.csv(file.path(PSDS_PATH, 'data', 'imanishi_data.csv'))

통계적 유의성과 p값

통계적 유의성이란, 자신의 실험 결과가 우연히 일어난 것인지 아니면 극단적인 것인지를 판단하는 방법. 결과가 우연히 벌어질 수 있는 변동성의 바깥에 존재한다면 (우연히 일어날 확률이 낮다면) 우리는 이것을 통계적으로 유의하다고 말한다.

웹 테스트 결과를 가지고 분석해보자.

전자 상거래 실험 결과

가격 A : 전환 200 / 전환되지 않음 23539
가격 B : 전환 182 / 전환되지 않음 22406

가격 A는 가격 B에 비해 약 5% 정도 우수한 결과를 보였다. 재표본추출 절차를 사용하면 가격 A와 B 간의 전환 차이가 우연에 의한 것인지 검정할 수 있다

perm_fun <- function(x, n1, n2)
{
  n <- n1 + n2
  idx_b <- sample(1:n, n1)
  idx_a <- sample(1:n, idx_b)
  mean_diff <- mean(x[idx_b]) - mean(x[idx_a])
  return(mean_diff)
}

obs_pct_diff <- 100*(200/23739 - 182/22588)
conversion <- c(rep(0, 45945), rep(1, 382))
perm_diffs <- rep(0, 1000)
for(i in 1:1000)
 perm_diffs[i] = 100*perm_fun(conversion, 23739, 22588)
hist(perm_diffs, xlab="Conversion rate (percent)", main='')
abline(v = obs_pct_diff, lty=2, lwd=1.5)
text(" Observed\n difference", x=obs_pct_diff, y=par()$usr[4]-20, adj=0)

관찰된 0.0368%의 차이는 랜덤 변이의 범위 밖에 있다 (통계적으로 유의함)


p 값

p 값은 통계적 유의성을 정확히 측정하기 위한 지표가 필요함

mean(perm_diffs > obs_pct_diff)
## [1] 0.293

p-value는 0.284이다. 즉, 우연히 얻은 결과의 약 30% 정도가 관찰한 것만큼 극단적이거나 그 이상 극단적인 결과를 얻을 것으로 기대된다.

사실, 이 경우에는 p값을 얻기 위해 순열검정을 할 필요가 없다. 대신 정규분포를 사용하여 p값을 근사할 수도 있다.

prop.test(x=c(200,182), n=c(23739,22588), alternative='greater')
## 
##  2-sample test for equality of proportions with continuity correction
## 
## data:  c(200, 182) out of c(23739, 22588)
## X-squared = 0.14893, df = 1, p-value = 0.3498
## alternative hypothesis: greater
## 95 percent confidence interval:
##  -0.001057439  1.000000000
## sample estimates:
##      prop 1      prop 2 
## 0.008424955 0.008057376

인수 x는 각 그룹의 성공횟수이고, 인수 n은 시행 횟수이다.

정규근사법을 통해, 순열검정에서 얻은 p값과 비슷한 0.3498을 얻은 것을 볼 수 있다.


t 검정

t-test는 데이터가 수치형인 아주 일반적인 2표본 비교에 주료 사용됨

t.test(Time ~ Page, data=session_times, alternative='less')
## 
##  Welch Two Sample t-test
## 
## data:  Time by Page
## t = -1.0983, df = 27.693, p-value = 0.1408
## alternative hypothesis: true difference in means is less than 0
## 95 percent confidence interval:
##      -Inf 19.59674
## sample estimates:
## mean in group Page A mean in group Page B 
##             126.3333             162.0000

대안가설은 페이지 A에 대한 평균 세션이 B보다 작다는 것이다.

순열검정을 통해 얻은 값과 매우 유사하다.


분산분석 (ANOVA)

여러 그룹 간의 통계적으로 유의미한 차이를 검정하는 통계적 절차

ggplot(four_sessions, aes(x=Page, y=Time)) +
  geom_boxplot()

한 쌍씩 비교하는 횟수가 증가할수록 우연히 일어난 일에 속을 가능성이 커진다.

전체적인 총괄검정을 위해 ANOVA 실시

summary(aovp(Time ~ Page, data=four_sessions))
## [1] "Settings:  unique SS "
## Component 1 :
##             Df R Sum Sq R Mean Sq Iter Pr(Prob)  
## Page         3    831.4    277.13 4086   0.0793 .
## Residuals   16   1618.4    101.15                
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Pr(Prob) 값이 바로 p값으로 결과는 0.07123


F 통계량


F 통계량은 잔차 오차 residual error로 인한 분산과 그룹 평균의 분산에 대한 비율을 기초로 한다.

이 비율이 높을수록 통계적으로 유의미함

summary(aov(Time ~ Page, data=four_sessions))
##             Df Sum Sq Mean Sq F value Pr(>F)  
## Page         3  831.4   277.1    2.74 0.0776 .
## Residuals   16 1618.4   101.2                 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Df는 자유도, Sum Sq는 제곱합, Mean Sq는 평균 제곱, F value는 F 통계량 가르킴

카이제곱검정 (Chi-Square)


clicks <- matrix(click_rate$Rate, nrow=3, ncol=2, byrow=TRUE)
dimnames(clicks) <- list(unique(click_rate$Headline), unique(click_rate$Click))

chisq.test(clicks, simulate.p.value=TRUE) # 재표본추출 실행
## 
##  Pearson's Chi-squared test with simulated p-value (based on 2000
##  replicates)
## 
## data:  clicks
## X-squared = 1.6659, df = NA, p-value = 0.4668

검정 결과는 관찰된 결과가 귀무가설(랜덤)로부터 얼마든지 얻을 수 있는 결과임을 보여준다.

x <- seq(1, 30, length=100)
chi <- data.frame(df = factor(rep(c(1, 2, 5, 10), rep(100, 4))),
                  x = rep(x, 4),
                  p = c(dchisq(x, 1), dchisq(x, 2), dchisq(x, 5), dchisq(x, 20)))

ggplot(chi, aes(x=x, y=p)) +
  geom_line(aes(linetype=df)) +
  theme_bw() +
  labs(x='', y='')

chisq.test(clicks, simulate.p.value=FALSE)
## 
##  Pearson's Chi-squared test
## 
## data:  clicks
## X-squared = 1.6659, df = 2, p-value = 0.4348

p값를 보면, 재표본추출해서 얻은 p값보다 약간 작다.

이는 카이제곱분포가 실제 통계 분포가 아니라 근사치이기 때문이다.


피셔의 정확검정

이렇듯 카이제곱분포는 재표본 검정의 좋은 근사치를 제공한다.

사건 발생 횟수가 매우 낮을 때는 예외이지만,
이런 예외적인 경우에도 재표본추출 방법을 통해 더 정확한 p값을 얻을 수 있다.

이를 위대한 통계학자 피셔의 이름을 붙여, 피셔의 정확검정이라고 한다.

fisher.test(clicks)
## 
##  Fisher's Exact Test for Count Data
## 
## data:  clicks
## p-value = 0.4824
## alternative hypothesis: two.sided

이렇게 얻은 p값은 0.4824로 재표본추출로 얻은 p값 0.4803과 매우 가깝다.