2-비율 가설 검정은 두 독립적인 표본 집단에서 관찰된 비율의 차이가 통계적으로 유의미한지를 검증하는 통계적 방법입니다.
1 Shiny 앱
#| label: shinylive-two-prop-testing
#| viewerWidth: 800
#| viewerHeight: 700
#| standalone: true
library(shiny)
library(showtext)
# font_add_google(name = "Nanum Gothic", regular.wt = 400)
showtext_auto()
library(gt)
# 1. 입력 데이터 변환---------------
extract <- function(text) {
text <- gsub(" ", "", text)
split <- strsplit(text, ",", fixed = FALSE)[[1]]
as.numeric(split)
}
# 2. 1-표본 t-검정---------------
t.test2 <- function(x, V, m0 = 0, alpha = 0.05, alternative = "two.sided") {
M <- mean(x)
n <- length(x)
sigma <- sqrt(V)
S <- sqrt(V / n)
statistic <- (M - m0) / S
p <- if (alternative == "two.sided") {
2 * pnorm(abs(statistic), lower.tail = FALSE)
} else if (alternative == "less") {
pnorm(statistic, lower.tail = TRUE)
} else {
pnorm(statistic, lower.tail = FALSE)
}
# p <- (1 - pnorm((M-m0)/S))
LCL <- (M - S * qnorm(1 - alpha / 2))
UCL <- (M + S * qnorm(1 - alpha / 2))
value <- list(mean = M, m0 = m0, sigma = sigma, statistic = statistic, p.value = p, LCL = LCL, UCL = UCL, alternative = alternative)
# print(sprintf("P-value = %g",p))
# print(sprintf("Lower %.2f%% Confidence Limit = %g",
# alpha, LCL))
# print(sprintf("Upper %.2f%% Confidence Limit = %g",
# alpha, UCL))
return(value)
}
# 3. 2-표본 t-검정---------------
t.test3 <- function(x, y, V1, V2, m0 = 0, alpha = 0.05, alternative = "two.sided") {
M1 <- mean(x)
M2 <- mean(y)
n1 <- length(x)
n2 <- length(y)
sigma1 <- sqrt(V1)
sigma2 <- sqrt(V2)
S <- sqrt((V1 / n1) + (V2 / n2))
statistic <- (M1 - M2 - m0) / S
p <- if (alternative == "two.sided") {
2 * pnorm(abs(statistic), lower.tail = FALSE)
} else if (alternative == "less") {
pnorm(statistic, lower.tail = TRUE)
} else {
pnorm(statistic, lower.tail = FALSE)
}
# p <- (1 - pnorm((M-m0)/S))
LCL <- (M1 - M2 - S * qnorm(1 - alpha / 2))
UCL <- (M1 - M2 + S * qnorm(1 - alpha / 2))
value <- list(mean1 = M1, mean2 = M2, m0 = m0, sigma1 = sigma1, sigma2 = sigma2, S = S, statistic = statistic, p.value = p, LCL = LCL, UCL = UCL, alternative = alternative)
# print(sprintf("P-value = %g",p))
# print(sprintf("Lower %.2f%% Confidence Limit = %g",
# alpha, LCL))
# print(sprintf("Upper %.2f%% Confidence Limit = %g",
# alpha, UCL))
return(value)
}
# 3. 1-표본 z-비율검정---------------
prop.z.test <- function(x, n, p0 = 0.5, conf.level = 0.95, alternative = "two.sided") {
ts.z <- NULL
cint <- NULL
p.val <- NULL
phat <- x / n
qhat <- 1 - phat
SE.phat <- sqrt((phat * qhat) / n)
ts.z <- (phat - p0) / SE.phat
p.val <- if (alternative == "two.sided") {
2 * pnorm(abs(ts.z), lower.tail = FALSE)
} else if (alternative == "less") {
pnorm(ts.z, lower.tail = TRUE)
} else {
pnorm(ts.z, lower.tail = FALSE)
}
cint <- phat + c(
-1 * ((qnorm(((1 - conf.level) / 2) + conf.level)) * SE.phat),
((qnorm(((1 - conf.level) / 2) + conf.level)) * SE.phat)
)
return(list(x = x, n = n, estimate = phat, null.value = p0, stderr = SE.phat, statistic = ts.z, p.value = p.val, conf.int = cint))
}
# 4. 2-표본 z-비율검정---------------
prop.z.test2 <- function(x1, x2, n1, n2, p0 = 0, pooled.stderr = TRUE, conf.level = 0.95, alternative = "two.sided") {
ts.z <- NULL
cint <- NULL
p.val <- NULL
phat1 <- x1 / n1
qhat1 <- 1 - phat1
phat2 <- x2 / n2
qhat2 <- 1 - phat2
pooled.phat <- ((n1 * phat1) + (n2 * phat2)) / (n1 + n2)
pooled.qhat <- 1 - pooled.phat
if (pooled.stderr == FALSE) {
SE.phat <- sqrt((phat1 * qhat1) / n1 + (phat2 * qhat2) / n2)
} else {
SE.phat <- sqrt(pooled.phat * pooled.qhat * (1 / n1 + 1 / n2))
}
ts.z <- (phat1 - phat2 - p0) / SE.phat
p.val <- if (alternative == "two.sided") {
2 * pnorm(abs(ts.z), lower.tail = FALSE)
} else if (alternative == "less") {
pnorm(ts.z, lower.tail = TRUE)
} else {
pnorm(ts.z, lower.tail = FALSE)
}
cint <- (phat1 - phat2) + c(
-1 * ((qnorm(((1 - conf.level) / 2) + conf.level)) * SE.phat),
((qnorm(((1 - conf.level) / 2) + conf.level)) * SE.phat)
)
return(list(x1 = x1, x2 = x2, n1 = n1, n2 = n2, estimate1 = phat1, estimate2 = phat2, null.value = p0, stderr = SE.phat, pooled.phat = pooled.phat, statistic = ts.z, p.value = p.val, conf.int = cint))
}
# 5. z-비율검정---------------
prop.z.test3 <- function(x, n, p0 = 0.5, conf.level = 0.95, alternative = "two.sided") {
ts.z <- NULL
cint <- NULL
p.val <- NULL
phat <- x / n
qhat <- 1 - phat
SE.phat <- sqrt((p0 * (1-p0)) / n)
ts.z <- (phat - p0) / SE.phat
p.val <- if (alternative == "two.sided") {
2 * pnorm(abs(ts.z), lower.tail = FALSE)
} else if (alternative == "less") {
pnorm(ts.z, lower.tail = TRUE)
} else {
pnorm(ts.z, lower.tail = FALSE)
}
return(list(x = x, n = n, estimate = phat, null.value = p0, stderr = SE.phat, statistic = ts.z, p.value = p.val))
}
two_proportion_UI <- function(id) {
ns <- NS(id)
# 1. 메인 패널 ------------------------------------
mainPanel <- mainPanel(
width = 9,
tabsetPanel(
tabPanel("검정결과",
uiOutput(ns("results_two_proportionn"))
),
tabPanel("그래프",
plotOutput(ns("plot_two_proportion"))
),
tabPanel("표본 데이터",
gt_output(ns("table_two_proportion"))
)
)
)
# 2. 옆 패널 --------------------------------------
sidebarPanel <- sidebarPanel(width = 3,
tags$br(),
tags$h4("* 데이터"),
tags$br(),
tags$b("표본 크기 1"),
numericInput( ns("n1_twoprop"), "\\(n_1 = \\)",
value = 30, min = 0, step = 1),
tags$b("표본 크기 2"),
numericInput( ns("n2_twoprop"), "\\(n_2 = \\)",
value = 30, min = 0, step = 1),
tags$hr(),
radioButtons(
inputId = ns("propx_twoprop"),
label = NULL,
choices = c(
"성공 비율 \\(\\hat{p}\\)" = "prop_true",
"성공 횟수 \\(x\\)" = "prop_false"
)
),
conditionalPanel(
# condition = "input.propx_twoprop == 'prop_true'",
condition = paste0("input['", ns("propx_twoprop"), "'] == 'prop_true'"),
tags$b("성공 비율"),
numericInput( ns("p1_twoprop"), "\\(\\hat{p}_1 = \\)",
value = 0.2, min = 0, max = 1, step = 0.01
),
numericInput( ns("p2_twoprop"), "\\(\\hat{p}_2 = \\)",
value = 0.3, min = 0, max = 1, step = 0.01
)
),
conditionalPanel(
# condition = "input.propx_twoprop == 'prop_false'",
condition = paste0("input['", ns("propx_twoprop"), "'] == 'prop_false'"),
tags$b("성공 횟수"),
numericInput( ns("x1_twoprop"), "\\(x_1 = \\)",
value = 10, min = 0, step = 1
),
numericInput( ns("x2_twoprop"), "\\(x_2 = \\)",
value = 12, min = 0, step = 1
)
),
tags$h4("* 가설검정"),
tags$p("1. 귀무가설"),
tags$p("\\( H_0 : p_1 - p_2 = \\)"),
numericInput( ns("twoprop_h0"),
label = NULL,
value = 0.1, step = 0.1, width="100px"),
checkboxInput( ns("pooledstderr_twoprop"), "합동(pooled) 표준오차 사용:", FALSE),
tags$p("2. 검정방향"),
radioButtons(
inputId = ns("twoprop_alternative"),
label = "대립가설",
inline = TRUE,
choices = c(
"\\( \\neq \\)" = "two.sided",
"\\( > \\)" = "greater",
"\\( < \\)" = "less"
)
),
tags$p("3. 유의수준"),
sliderInput( ns("twoprop_alpha"),
"유의수준 \\(\\alpha = \\)",
min = 0.01,
max = 0.10,
step = 0.01,
value = 0.05
)
)
# 레이아웃 -----------------------------------------------------------
tagList(
withMathJax(),
tags$div(
fluidPage(
theme = shinythemes::shinytheme("flatly"),
sidebarPanel,
mainPanel
)
)
)
}
two_proportion_server <- function(id) {
moduleServer(id, function(input, output, session) {
# 1. 보고서 ----------------------------------------------------------------
output$results_two_proportionn <- renderUI({
if (input$propx_twoprop == "prop_true" & input$pooledstderr_twoprop == FALSE) {
test <- prop.z.test2(x1 = input$n1_twoprop * input$p1_twoprop, x2 = input$n2_twoprop * input$p2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = FALSE)
test_confint <- prop.z.test2(x1 = input$n1_twoprop * input$p1_twoprop, x2 = input$n2_twoprop * input$p2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = "two.sided", pooled.stderr = FALSE)
withMathJax(
tags$h3("데이터"),
br(),
paste0("\\(n_1 =\\) ", round(test$n1, 3)),
br(),
paste0("\\(n_2 =\\) ", round(test$n2, 3)),
br(),
paste0("\\(\\hat{p}_1 =\\) ", round(test$estimate1, 3)),
br(),
paste0("\\(\\hat{p}_2 =\\) ", round(test$estimate2, 3)),
br(),
paste0("\\(\\hat{q}_1 = 1 - \\hat{p}_1 =\\) ", round(1 - test$estimate1, 3)),
br(),
paste0("\\(\\hat{q}_2 = 1 - \\hat{p}_2 =\\) ", round(1 - test$estimate2, 3)),
br(),
helpText(paste0("\\( n_1\\hat{p}_1 = \\) ", round(test$n1 * test$estimate1, 3),
" 와 \\( n_1(1-\\hat{p}_1) = \\) ", round(test$n1 * (1 - test$estimate1), 3))),
helpText(paste0("\\( n_2\\hat{p}_2 = \\) ", round(test$n2 * test$estimate2, 3),
" 와 \\( n_2(1-\\hat{p}_2) = \\) ", round(test$n2 * (1 - test$estimate2), 3))),
helpText(paste0("\\( n_1\\hat{p}_1 \\geq 5\\), \\( n_1(1-\\hat{p}_1) \\geq 5\\), \\( n_2\\hat{p}_2 \\geq 5\\) 와 \\( n_2(1-\\hat{p}_2) \\geq 5\\) 가정이 ",
ifelse(test$n1 * test$estimate1 >= 5 & test$n1 * (1 - test$estimate1) >= 5 & test$n2 * test$estimate2 >= 5 & test$n2 * (1 - test$estimate2) >= 5, " 충족됨.", " 총족되지 않음."))),
br(),
tags$h3("신뢰구간 - 양측"),
br(),
paste0(
(1 - input$twoprop_alpha) * 100, "% 신뢰구간: \\(p_1 - p_2 = \\hat{p}_1 - \\hat{p}_2 \\pm z_{\\alpha/2} \\sqrt{\\dfrac{\\hat{p}_1(1-\\hat{p}_1)}{n_1} + \\dfrac{\\hat{p}_2(1-\\hat{p}_2)}{n_2}} = \\) ",
round(test_confint$estimate1, 3), ifelse(test_confint$estimate2 >= 0, paste0(" - ", round(test_confint$estimate2, 3)), paste0(" + ", round(abs(test_confint$estimate2), 3))), " \\( \\pm \\) ", "\\( ( \\)", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), " * ", round(test_confint$stderr, 3), "\\( ) \\) ", "\\( = \\) ",
"[", round(test_confint$conf.int[1], 3), "; ", round(test_confint$conf.int[2], 3), "]"
),
br(),
br(),
tags$h3("가설 검정"),
br(),
paste0("1. \\(H_0 : p_1 - p_2 = \\) ", test$null.value, " vs. \\(H_1 : p_1 - p_2 \\) ", ifelse(input$twoprop_alternative == "two.sided", "\\( \\neq \\) ", ifelse(input$twoprop_alternative == "greater", "\\( > \\) ", "\\( < \\) ")), test$null.value),
br(),
paste0(
"2. 검정 통계량 : \\(z_{obs} = \\dfrac{(\\hat{p}_1 - \\hat{p}_2) - (p_1 - p_2)}{\\sqrt{\\dfrac{\\hat{p}_1(1-\\hat{p}_1)}{n_1} + \\dfrac{\\hat{p}_2(1-\\hat{p}_2)}{n_2}}} = \\) ",
"(", round(test$estimate1, 3), ifelse(test$estimate2 >= 0, paste0(" - ", round(test$estimate2, 3)), paste0(" + ", round(abs(test$estimate2), 3))), ifelse(test$null.value >= 0, paste0(" - ", test$null.value), paste0(" + ", abs(test$null.value))), ") / ", round(test$stderr, 3), " \\( = \\) ",
ifelse(test$null.value >= -1 & test$null.value <= 1, round(test$statistic, 3), "Error: \\( p_1 - p_2 \\) must be \\( -1 \\leq p_1 - p_2 \\leq 1\\)")
),
br(),
paste0(
"3. 임계값 :", ifelse(input$twoprop_alternative == "two.sided", " \\( \\pm z_{\\alpha/2} = \\pm z(\\)", ifelse(input$twoprop_alternative == "greater", " \\( z_{\\alpha} = z(\\)", " \\( -z_{\\alpha} = -z(\\)")),
ifelse(input$twoprop_alternative == "two.sided", input$twoprop_alpha / 2, input$twoprop_alpha), "\\()\\)", " \\( = \\) ",
ifelse(input$twoprop_alternative == "two.sided", "\\( \\pm \\)", ifelse(input$twoprop_alternative == "greater", "", " -")),
ifelse(input$twoprop_alternative == "two.sided", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), round(qnorm(input$twoprop_alpha, lower.tail = FALSE), 3))
),
br(),
paste0("4. 결론 : ", ifelse(test$p.value < input$twoprop_alpha, "\\(H_0\\) 기각.", "\\(H_0\\) 기각 못함.")),
br(),
br(),
tags$h3("해석"),
br(),
paste0( input$twoprop_alpha * 100, "% 유의수준에서, ",
"비율에서 차이가 ", test$null.value, " 이라는 귀무가설을 ",
ifelse(test$p.value < input$twoprop_alpha, "기각한다", "기각하지 않는다"),
" \\((p\\)-값 ", ifelse(test$p.value < 0.001, "< 0.001", paste0("\\(=\\) ", round(test$p.value, 3))), ")", ".")
)
} else if ( input$propx_twoprop == "prop_false" & input$pooledstderr_twoprop == FALSE) {
test <- prop.z.test2(x1 = input$x1_twoprop, x2 = input$x2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = FALSE)
test_confint <- prop.z.test2(x1 = input$x1_twoprop, x2 = input$x2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = "two.sided", pooled.stderr = FALSE)
withMathJax(
tags$h2("데이터"),
br(),
paste0("\\(n_1 =\\) ", round(test$n1, 3)),
br(),
paste0("\\(n_2 =\\) ", round(test$n2, 3)),
br(),
paste0("\\(\\hat{p}_1 = \\dfrac{x_1}{n_1} = \\) ", test$x1, " \\( / \\) ", test$n1, " \\( = \\) ", round(test$estimate1, 3)),
br(),
paste0("\\(\\hat{p}_2 = \\dfrac{x_2}{n_2} = \\) ", test$x2, " \\( / \\) ", test$n2, " \\( = \\) ", round(test$estimate2, 3)),
br(),
paste0("\\(\\hat{q}_1 = 1 - \\hat{p}_1 =\\) ", round(1 - test$estimate1, 3)),
br(),
paste0("\\(\\hat{q}_2 = 1 - \\hat{p}_2 =\\) ", round(1 - test$estimate2, 3)),
br(),
helpText(paste0("\\( n_1\\hat{p}_1 = \\) ", round(test$n1 * test$estimate1, 3), " 와 \\( n_1(1-\\hat{p}_1) = \\) ", round(test$n1 * (1 - test$estimate1), 3))),
helpText(paste0("\\( n_2\\hat{p}_2 = \\) ", round(test$n2 * test$estimate2, 3), " 와 \\( n_2(1-\\hat{p}_2) = \\) ", round(test$n2 * (1 - test$estimate2), 3))),
helpText(paste0("\\( n_1\\hat{p}_1 \\geq 5\\), \\( n_1(1-\\hat{p}_1) \\geq 5\\), \\( n_2\\hat{p}_2 \\geq 5\\) 와 \\( n_2(1-\\hat{p}_2) \\geq 5\\) 가정이 ", ifelse(test$n1 * test$estimate1 >= 5 & test$n1 * (1 - test$estimate1) >= 5 & test$n2 * test$estimate2 >= 5 & test$n2 * (1 - test$estimate2) >= 5, " 충족됨.", " 충족되지 않음."))),
br(),
tags$h2("신뢰구간 - 양측"),
br(),
paste0(
(1 - input$twoprop_alpha) * 100, "% CI for \\(p_1 - p_2 = \\hat{p}_1 - \\hat{p}_2 \\pm z_{\\alpha/2} \\sqrt{\\dfrac{\\hat{p}_1(1-\\hat{p}_1)}{n_1} + \\dfrac{\\hat{p}_2(1-\\hat{p}_2)}{n_2}} = \\) ",
round(test_confint$estimate1, 3), ifelse(test_confint$estimate2 >= 0, paste0(" - ", round(test_confint$estimate2, 3)), paste0(" + ", round(abs(test_confint$estimate2), 3))), " \\( \\pm \\) ", "\\( ( \\)", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), " * ", round(test_confint$stderr, 3), "\\( ) \\) ", "\\( = \\) ",
"[", round(test_confint$conf.int[1], 3), "; ", round(test_confint$conf.int[2], 3), "]"
),
br(),
br(),
tags$h2("가설 검정"),
br(),
paste0("1. \\(H_0 : p_1 - p_2 = \\) ", test$null.value, " vs. \\(H_1 : p_1 - p_2 \\) ", ifelse(input$twoprop_alternative == "two.sided", "\\( \\neq \\) ", ifelse(input$twoprop_alternative == "greater", "\\( > \\) ", "\\( < \\) ")), test$null.value),
br(),
paste0(
"2. 검정 통계량 : \\(z_{obs} = \\dfrac{(\\hat{p}_1 - \\hat{p}_2) - (p_1 - p_2)}{\\sqrt{\\dfrac{\\hat{p}_1(1-\\hat{p}_1)}{n_1} + \\dfrac{\\hat{p}_2(1-\\hat{p}_2)}{n_2}}} = \\) ",
"(", round(test$estimate1, 3), ifelse(test$estimate2 >= 0, paste0(" - ", round(test$estimate2, 3)), paste0(" + ", round(abs(test$estimate2), 3))), ifelse(test$null.value >= 0, paste0(" - ", test$null.value), paste0(" + ", abs(test$null.value))), ") / ", round(test$stderr, 3), " \\( = \\) ",
ifelse(test$null.value >= -1 & test$null.value <= 1, round(test$statistic, 3), "Error: \\( p_1 - p_2 \\) must be \\( -1 \\leq p_1 - p_2 \\leq 1\\)")
),
br(),
paste0(
"3. 임계값 :", ifelse(input$twoprop_alternative == "two.sided", " \\( \\pm z_{\\alpha/2} = \\pm z(\\)", ifelse(input$twoprop_alternative == "greater", " \\( z_{\\alpha} = z(\\)", " \\( -z_{\\alpha} = -z(\\)")),
ifelse(input$twoprop_alternative == "two.sided", input$twoprop_alpha / 2, input$twoprop_alpha), "\\()\\)", " \\( = \\) ",
ifelse(input$twoprop_alternative == "two.sided", "\\( \\pm \\)", ifelse(input$twoprop_alternative == "greater", "", " -")),
ifelse(input$twoprop_alternative == "two.sided", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), round(qnorm(input$twoprop_alpha, lower.tail = FALSE), 3))
),
br(),
paste0("4. 결론 : ", ifelse(test$p.value < input$twoprop_alpha, "\\(H_0\\) 기각.", "\\(H_0\\) 기각 못함.")),
br(),
br(),
tags$h2("해석"),
br(),
paste0( input$twoprop_alpha * 100, "% 유의수준에서, ",
"비율에서 차이가 ", test$null.value, " 이라는 귀무가설을 ",
ifelse(test$p.value < input$twoprop_alpha, "기각한다", "기각하지 않는다"),
" \\((p\\)-값 ", ifelse(test$p.value < 0.001, "< 0.001", paste0("\\(=\\) ", round(test$p.value, 3))), ")", ".")
)
} else if ( input$propx_twoprop == "prop_true" & input$pooledstderr_twoprop == TRUE) {
test <- prop.z.test2(x1 = input$n1_twoprop * input$p1_twoprop, x2 = input$n2_twoprop * input$p2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = TRUE)
test_confint <- prop.z.test2(x1 = input$n1_twoprop * input$p1_twoprop, x2 = input$n2_twoprop * input$p2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = "two.sided", pooled.stderr = FALSE)
withMathJax(
tags$h2("데이터"),
br(),
paste0("\\(n_1 =\\) ", round(test$n1, 3)),
br(),
paste0("\\(n_2 =\\) ", round(test$n2, 3)),
br(),
paste0("\\(\\hat{p}_1 =\\) ", round(test$estimate1, 3)),
br(),
paste0("\\(\\hat{p}_2 =\\) ", round(test$estimate2, 3)),
br(),
paste0("\\(\\hat{q}_1 = 1 - \\hat{p}_1 =\\) ", round(1 - test$estimate1, 3)),
br(),
paste0("\\(\\hat{q}_2 = 1 - \\hat{p}_2 =\\) ", round(1 - test$estimate2, 3)),
br(),
helpText(paste0("\\( n_1\\hat{p}_1 = \\) ", round(test$n1 * test$estimate1, 3), " 와 \\( n_1(1-\\hat{p}_1) = \\) ", round(test$n1 * (1 - test$estimate1), 3))),
helpText(paste0("\\( n_2\\hat{p}_2 = \\) ", round(test$n2 * test$estimate2, 3), " 와 \\( n_2(1-\\hat{p}_2) = \\) ", round(test$n2 * (1 - test$estimate2), 3))),
helpText(paste0("\\( n_1\\hat{p}_1 \\geq 5\\), \\( n_1(1-\\hat{p}_1) \\geq 5\\), \\( n_2\\hat{p}_2 \\geq 5\\) 와 \\( n_2(1-\\hat{p}_2) \\geq 5\\) 가정이", ifelse(test$n1 * test$estimate1 >= 5 & test$n1 * (1 - test$estimate1) >= 5 & test$n2 * test$estimate2 >= 5 & test$n2 * (1 - test$estimate2) >= 5, " 충족됨.", " 충족되지 않음."))),
br(),
tags$h2("신뢰구간 - 양측"),
br(),
paste0(
(1 - input$twoprop_alpha) * 100, "% CI for \\(p_1 - p_2 = \\hat{p}_1 - \\hat{p}_2 \\pm z_{\\alpha/2} \\sqrt{\\dfrac{\\hat{p}_1(1-\\hat{p}_1)}{n_1} + \\dfrac{\\hat{p}_2(1-\\hat{p}_2)}{n_2}} = \\) ",
round(test_confint$estimate1, 3), ifelse(test_confint$estimate2 >= 0, paste0(" - ", round(test_confint$estimate2, 3)), paste0(" + ", round(abs(test_confint$estimate2), 3))), " \\( \\pm \\) ", "\\( ( \\)", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), " * ", round(test_confint$stderr, 3), "\\( ) \\) ", "\\( = \\) ",
"[", round(test_confint$conf.int[1], 3), "; ", round(test_confint$conf.int[2], 3), "]"
),
br(),
br(),
tags$h2("가설 검정"),
br(),
paste0("1. \\(H_0 : p_1 - p_2 = \\) ", test$null.value, " vs. \\(H_1 : p_1 - p_2 \\) ", ifelse(input$twoprop_alternative == "two.sided", "\\( \\neq \\) ", ifelse(input$twoprop_alternative == "greater", "\\( > \\) ", "\\( < \\) ")), test$null.value),
br(),
paste0("2. 검정 통계량 : \\(z_{obs} = \\dfrac{(\\hat{p}_1 - \\hat{p}_2) - (p_1 - p_2)}{\\sqrt{\\hat{p}(1-\\hat{p})\\Big(\\dfrac{1}{n_1} + \\dfrac{1}{n_2}\\Big)}} \\) "),
br(),
paste0("여기서 ", "\\( \\hat{p} = \\dfrac{n_1 \\hat{p}_1 + n_2 \\hat{p}_2}{n_1 + n_2} = \\) ", "(", test$n1, " * ", round(test$estimate1, 3), " + ", test$n2, " * ", round(test$estimate2, 3), ") / (", test$n1, " + ", test$n2, ") = ", round(test$pooled.phat, 3)),
br(),
paste0(
"\\( \\Rightarrow z_{obs} = \\dfrac{(\\hat{p}_1 - \\hat{p}_2) - (p_1 - p_2)}{\\sqrt{\\hat{p}(1-\\hat{p})\\Big(\\dfrac{1}{n_1} + \\dfrac{1}{n_2}\\Big)}} = \\) ",
"(", round(test$estimate1, 3), ifelse(test$estimate2 >= 0, paste0(" - ", round(test$estimate2, 3)), paste0(" + ", round(abs(test$estimate2), 3))), ifelse(test$null.value >= 0, paste0(" - ", test$null.value), paste0(" + ", abs(test$null.value))), ") / ", round(test$stderr, 3), " \\( = \\) ",
ifelse(test$null.value >= -1 & test$null.value <= 1, round(test$statistic, 3), "Error: \\( p_1 - p_2 \\) must be \\( -1 \\leq p_1 - p_2 \\leq 1\\)")
),
br(),
paste0(
"3. 임계값 :", ifelse(input$twoprop_alternative == "two.sided", " \\( \\pm z_{\\alpha/2} = \\pm z(\\)", ifelse(input$twoprop_alternative == "greater", " \\( z_{\\alpha} = z(\\)", " \\( -z_{\\alpha} = -z(\\)")),
ifelse(input$twoprop_alternative == "two.sided", input$twoprop_alpha / 2, input$twoprop_alpha), "\\()\\)", " \\( = \\) ",
ifelse(input$twoprop_alternative == "two.sided", "\\( \\pm \\)", ifelse(input$twoprop_alternative == "greater", "", " -")),
ifelse(input$twoprop_alternative == "two.sided", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), round(qnorm(input$twoprop_alpha, lower.tail = FALSE), 3))
),
br(),
paste0("4. 결론 : ", ifelse(test$p.value < input$twoprop_alpha, "\\(H_0\\) 기각.", "\\(H_0\\) 기각 못함.")),
br(),
br(),
tags$h2("해석"),
br(),
paste0( input$twoprop_alpha * 100, "% 유의수준에서, ",
"비율에서 차이가 ", test$null.value, " 이라는 귀무가설을 ",
ifelse(test$p.value < input$twoprop_alpha, "기각한다", "기각하지 않는다"),
" \\((p\\)-값 ", ifelse(test$p.value < 0.001, "< 0.001", paste0("\\(=\\) ", round(test$p.value, 3))), ")", ".")
)
} else if ( input$propx_twoprop == "prop_false" & input$pooledstderr_twoprop == TRUE) {
test <- prop.z.test2(x1 = input$x1_twoprop, x2 = input$x2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = TRUE)
test_confint <- prop.z.test2(x1 = input$x1_twoprop, x2 = input$x2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = "two.sided", pooled.stderr = FALSE)
withMathJax(
tags$h2("데이터"),
br(),
paste0("\\(n_1 =\\) ", round(test$n1, 3)),
br(),
paste0("\\(n_2 =\\) ", round(test$n2, 3)),
br(),
paste0("\\(\\hat{p}_1 = \\dfrac{x_1}{n_1} = \\) ", test$x1, " \\( / \\) ", test$n1, " \\( = \\) ", round(test$estimate1, 3)),
br(),
paste0("\\(\\hat{p}_2 = \\dfrac{x_2}{n_2} = \\) ", test$x2, " \\( / \\) ", test$n2, " \\( = \\) ", round(test$estimate2, 3)),
br(),
paste0("\\(\\hat{q}_1 = 1 - \\hat{p}_1 =\\) ", round(1 - test$estimate1, 3)),
br(),
paste0("\\(\\hat{q}_2 = 1 - \\hat{p}_2 =\\) ", round(1 - test$estimate2, 3)),
br(),
helpText(paste0("\\( n_1\\hat{p}_1 = \\) ", round(test$n1 * test$estimate1, 3), " 와 \\( n_1(1-\\hat{p}_1) = \\) ", round(test$n1 * (1 - test$estimate1), 3))),
helpText(paste0("\\( n_2\\hat{p}_2 = \\) ", round(test$n2 * test$estimate2, 3), " 와 \\( n_2(1-\\hat{p}_2) = \\) ", round(test$n2 * (1 - test$estimate2), 3))),
helpText(paste0("\\( n_1\\hat{p}_1 \\geq 5\\), \\( n_1(1-\\hat{p}_1) \\geq 5\\), \\( n_2\\hat{p}_2 \\geq 5\\) 와 \\( n_2(1-\\hat{p}_2) \\geq 5\\) 가정이", ifelse(test$n1 * test$estimate1 >= 5 & test$n1 * (1 - test$estimate1) >= 5 & test$n2 * test$estimate2 >= 5 & test$n2 * (1 - test$estimate2) >= 5, " 충족됨.", " 충족되지 않음."))),
br(),
tags$h2("신뢰구간 - 양측"),
br(),
paste0(
(1 - input$twoprop_alpha) * 100, "% CI for \\(p_1 - p_2 = \\hat{p}_1 - \\hat{p}_2 \\pm z_{\\alpha/2} \\sqrt{\\dfrac{\\hat{p}_1(1-\\hat{p}_1)}{n_1} + \\dfrac{\\hat{p}_2(1-\\hat{p}_2)}{n_2}} = \\) ",
round(test_confint$estimate1, 3), ifelse(test_confint$estimate2 >= 0, paste0(" - ", round(test_confint$estimate2, 3)), paste0(" + ", round(abs(test_confint$estimate2), 3))), " \\( \\pm \\) ", "\\( ( \\)", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), " * ", round(test_confint$stderr, 3), "\\( ) \\) ", "\\( = \\) ",
"[", round(test_confint$conf.int[1], 3), "; ", round(test_confint$conf.int[2], 3), "]"
),
br(),
br(),
tags$h2("가설 검정"),
br(),
paste0("1. \\(H_0 : p_1 - p_2 = \\) ", test$null.value, " vs. \\(H_1 : p_1 - p_2 \\) ", ifelse(input$twoprop_alternative == "two.sided", "\\( \\neq \\) ", ifelse(input$twoprop_alternative == "greater", "\\( > \\) ", "\\( < \\) ")), test$null.value),
br(),
paste0("2. 검정 통계량 : \\(z_{obs} = \\dfrac{(\\hat{p}_1 - \\hat{p}_2) - (p_1 - p_2)}{\\sqrt{\\hat{p}(1-\\hat{p})\\Big(\\dfrac{1}{n_1} + \\dfrac{1}{n_2}\\Big)}} \\) "),
br(),
paste0("여기서 ", "\\( \\hat{p} = \\dfrac{n_1 \\hat{p}_1 + n_2 \\hat{p}_2}{n_1 + n_2} = \\) ", "(", test$n1, " * ", round(test$estimate1, 3), " + ", test$n2, " * ", round(test$estimate2, 3), ") / (", test$n1, " + ", test$n2, ") = ", round(test$pooled.phat, 3)),
br(),
paste0(
"\\( \\Rightarrow z_{obs} = \\dfrac{(\\hat{p}_1 - \\hat{p}_2) - (p_1 - p_2)}{\\sqrt{\\hat{p}(1-\\hat{p})\\Big(\\dfrac{1}{n_1} + \\dfrac{1}{n_2}\\Big)}} = \\) ",
"(", round(test$estimate1, 3), ifelse(test$estimate2 >= 0, paste0(" - ", round(test$estimate2, 3)), paste0(" + ", round(abs(test$estimate2), 3))), ifelse(test$null.value >= 0, paste0(" - ", test$null.value), paste0(" + ", abs(test$null.value))), ") / ", round(test$stderr, 3), " \\( = \\) ",
ifelse(test$null.value >= -1 & test$null.value <= 1, round(test$statistic, 3), "Error: \\( p_1 - p_2 \\) must be \\( -1 \\leq p_1 - p_2 \\leq 1\\)")
),
br(),
paste0(
"3. 임계값 :", ifelse(input$twoprop_alternative == "two.sided", " \\( \\pm z_{\\alpha/2} = \\pm z(\\)", ifelse(input$twoprop_alternative == "greater", " \\( z_{\\alpha} = z(\\)", " \\( -z_{\\alpha} = -z(\\)")),
ifelse(input$twoprop_alternative == "two.sided", input$twoprop_alpha / 2, input$twoprop_alpha), "\\()\\)", " \\( = \\) ",
ifelse(input$twoprop_alternative == "two.sided", "\\( \\pm \\)", ifelse(input$twoprop_alternative == "greater", "", " -")),
ifelse(input$twoprop_alternative == "two.sided", round(qnorm(input$twoprop_alpha / 2, lower.tail = FALSE), 3), round(qnorm(input$twoprop_alpha, lower.tail = FALSE), 3))
),
br(),
paste0("4. 결론 : ", ifelse(test$p.value < input$twoprop_alpha, "\\(H_0\\) 기각.", "\\(H_0\\) 기각 못함.")),
br(),
br(),
tags$h2("해석"),
br(),
paste0( input$twoprop_alpha * 100, "% 유의수준에서, ",
"비율에서 차이가 ", test$null.value, " 이라는 귀무가설을 ",
ifelse(test$p.value < input$twoprop_alpha, "기각한다", "기각하지 않는다"),
" \\((p\\)-값 ", ifelse(test$p.value < 0.001, "< 0.001", paste0("\\(=\\) ", round(test$p.value, 3))), ")", ".")
)
}
})
# 2. 시각화 ----------------------------------------------------------------
output$plot_two_proportion <- renderPlot({
if (input$propx_twoprop == "prop_true" & input$pooledstderr_twoprop == FALSE) {
test <- prop.z.test2(x1 = input$n1_twoprop * input$p1_twoprop, x2 = input$n2_twoprop * input$p2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = FALSE)
} else if (input$propx_twoprop == "prop_false" & input$pooledstderr_twoprop == FALSE) {
test <- prop.z.test2(x1 = input$x1_twoprop, x2 = input$x2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = FALSE)
} else if (input$propx_twoprop == "prop_true" & input$pooledstderr_twoprop == TRUE) {
test <- prop.z.test2(x1 = input$n1_twoprop * input$p1_twoprop, x2 = input$n2_twoprop * input$p2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = TRUE)
} else if (input$propx_twoprop == "prop_false" & input$pooledstderr_twoprop == TRUE) {
test <- prop.z.test2(x1 = input$x1_twoprop, x2 = input$x2_twoprop, n1 = input$n1_twoprop, n2 = input$n2_twoprop, p0 = input$twoprop_h0, conf.level = 1 - input$twoprop_alpha, alternative = input$twoprop_alternative, pooled.stderr = TRUE)
}
if (input$twoprop_alternative == "two.sided") {
funcShaded <- function(x) {
y <- dnorm(x, mean = 0, sd = 1)
y[x < qnorm(input$twoprop_alpha / 2, mean = 0, sd = 1, lower.tail = FALSE) & x > qnorm(input$twoprop_alpha / 2, mean = 0, sd = 1) ] <- NA
return(y)
}
} else if (input$twoprop_alternative == "greater") {
funcShaded <- function(x) {
y <- dnorm(x, mean = 0, sd = 1)
y[x < qnorm(input$twoprop_alpha, mean = 0, sd = 1, lower.tail = FALSE) ] <- NA
return(y)
}
} else if (input$twoprop_alternative == "less") {
funcShaded <- function(x) {
y <- dnorm(x, mean = 0, sd = 1)
y[x > qnorm(input$twoprop_alpha, mean = 0, sd = 1, lower.tail = TRUE) ] <- NA
return(y)
}
}
p <- ggplot(data.frame(x = c(qnorm(0.999, mean = 0, sd = 1, lower.tail = FALSE), qnorm(0.999, mean = 0, sd = 1, lower.tail = TRUE))), aes(x = x)) +
stat_function( fun = dnorm, fill="gray90", args = list(mean = 0, sd = 1)) +
stat_function( fun = funcShaded, geom = "area", fill = "sky blue", alpha = 0.8) +
theme_minimal() +
geom_vline(xintercept = test$statistic, color = "steelblue") +
geom_text(aes(x = test$statistic, label = paste0("검정 통계량 = ", round(test$statistic, 3)), y = 0.2), colour = "steelblue", angle = 90, vjust = 1.3, text = element_text(size = 11)) +
ggtitle(paste0("정규분포 N(0,1)")) +
theme(plot.title = element_text(face = "bold", hjust = 0.5)) +
ylab("밀도") +
xlab("x")
p
})
# 3. 표 ----------------------------------------------------------------
output$table_two_proportion <- render_gt({
prop_dat1 <- rbinom(input$n1_twoprop, 1, input$p1_twoprop)
prop_dat2 <- rbinom(input$n2_twoprop, 1, input$p2_twoprop)
tibble(변수1 = prop_dat1,
변수2 = prop_dat2 ) %>%
mutate(순번 = row_number()) |>
relocate(순번, .before = 변수1) |>
gt()
})
})
}
ui <- fluidPage(
titlePanel("2-비율 가설 검정"),
two_proportion_UI("two_prop")
)
server <- function(input, output, session) {
two_proportion_server("two_prop")
}
shinyApp(ui, server)
2 코딩
라이센스
CC BY-SA-NC & GPL-3