Files
admin 33e9543b15 Upload to Server
Uploading to server
2025-08-02 05:15:23 +07:00

210 lines
8.0 KiB
R

# Tải các thư viện cần thiết
library(shiny)
library(bslib)
library(ggplot2)
library(shinycssloaders)
library(pwr)
# --- Giao diện người dùng (UI) ---
ui <- fluidPage(
theme = bs_theme(version = 5, bootswatch = "cerulean"),
withMathJax(),
titlePanel("Tính toán Cỡ mẫu cho Kiểm định t hai mẫu độc lập"),
sidebarLayout(
sidebarPanel(
h4("Tham số đầu vào"),
numericInput("d",
label = "Effect Size (Cohen's d):",
value = 0.5, min = 0.1, step = 0.1),
helpText("Cohen's d đo lường độ lớn của sự khác biệt giữa hai trung bình. Quy ước: 0.2 (nhỏ), 0.5 (trung bình), 0.8 (lớn)."),
selectInput("alternative", "Loại kiểm định:",
choices = c("Hai phía (Two-sided)" = "two.sided",
"Lớn hơn (Greater)" = "greater",
"Nhỏ hơn (Less)" = "less")),
sliderInput("sig_level",
label = "Mức ý nghĩa (\\(\\alpha\\)):",
min = 0.01, max = 0.10, value = 0.05, step = 0.01),
sliderInput("power",
label = "Power mong muốn (\\(1 - \\beta\\)):",
min = 0.50, max = 0.99, value = 0.80, step = 0.01),
hr(),
helpText("Ứng dụng sử dụng hàm pwr.t.test() từ gói 'pwr'. Kết quả và đồ thị sẽ tự động cập nhật ngay lập tức.")
),
mainPanel(
tabsetPanel(
id = "results_tabs",
type = "pills",
tabPanel(
"Kết quả & Diễn giải",
h4("Kết quả tính toán"),
withSpinner(uiOutput("sample_size_output"), type = 6, color = "#007bff")
),
tabPanel(
"Đồ thị Power vs. Cỡ mẫu",
h4("Mối quan hệ giữa Power và Cỡ mẫu"),
withSpinner(plotOutput("power_plot"), type = 6, color = "#007bff")
),
tabPanel(
"Phân tích Effect Size",
h4("Mối quan hệ giữa Cỡ mẫu và Effect Size"),
p("Đồ thị này cho thấy cỡ mẫu cần thiết thay đổi như thế nào khi Effect Size thay đổi."),
withSpinner(plotOutput("effect_analysis_plot"), type = 6, color = "#007bff")
),
tabPanel(
"Giả thuyết và Công thức (Helper)",
h4("Giả thuyết của Kiểm định t hai mẫu"),
p("Kiểm định này so sánh trung bình của hai nhóm độc lập (\\(\\mu_1\\) và \\(\\mu_2\\))."),
p("$$H_0: \\mu_1 = \\mu_2$$"),
p("$$H_a: \\mu_1 \\neq \\mu_2 \\quad (\\text{hoặc } > \\text{ hoặc } <\\text{)}$$"),
hr(),
h4("Công thức tính toán"),
tags$b("1. Thống kê kiểm định (Test Statistic):"),
p("Giả định phương sai bằng nhau, giá trị thống kê t được tính như sau:"),
p("$$ t = \\frac{(\\bar{x}_1 - \\bar{x}_2)}{s_p \\sqrt{\\frac{1}{n_1} + \\frac{1}{n_2}}} $$"),
p("Trong đó \\(\\bar{x}_1, \\bar{x}_2\\) là trung bình mẫu; \\(n_1, n_2\\) là cỡ mẫu; và \\(s_p\\) là độ lệch chuẩn gộp (pooled standard deviation)."),
tags$b("2. Effect Size (Cohen's d):"),
p("Cohen's d cho trường hợp hai mẫu được định nghĩa là:"),
p("$$ d = \\frac{|\\mu_1 - \\mu_2|}{\\sigma} $$"),
p("Trong đó \\(\\sigma\\) là độ lệch chuẩn của tổng thể (giả định bằng nhau cho cả hai nhóm)."),
tags$b("3. Tính toán Power:"),
p("Ứng dụng này sử dụng hàm `pwr.t.test` với tham số `type = 'two.sample'`. Hàm này giải phương trình power dựa trên phân phối t phi trung tâm để tìm ra cỡ mẫu `n` cần thiết cho mỗi nhóm.")
)
)
)
)
)
# --- Logic của máy chủ (Server) ---
server <- function(input, output, session) {
# --- PHẦN TÍNH TOÁN CHÍNH ---
main_results <- reactive({
req(input$d, input$sig_level, input$power, input$alternative)
pwr_result <- tryCatch({
pwr.t.test(
d = input$d,
sig.level = input$sig_level,
power = input$power,
type = "two.sample",
alternative = input$alternative
)
}, error = function(e) NULL)
if (is.null(pwr_result)) return(NULL)
required_n <- ceiling(pwr_result$n)
sample_sizes <- seq(5, required_n + 50, by = 1)
power_data <- tryCatch({
pwr.t.test(
n = sample_sizes,
d = input$d,
sig.level = input$sig_level,
type = "two.sample",
alternative = input$alternative
)
}, error = function(e) NULL)
if(is.null(power_data)) return(list(required_n = required_n, power_plot_data = NULL))
list(
required_n = required_n,
power_plot_data = data.frame(SampleSize = power_data$n, Power = power_data$power)
)
})
output$sample_size_output <- renderUI({
res <- main_results()
if (is.null(res)) {
return(tags$div(class = "alert alert-danger", "Có lỗi xảy ra trong quá trình tính toán. Vui lòng kiểm tra lại các tham số."))
}
tagList(
tags$p("Để phát hiện một effect size (Cohen's d) là", tags$b(input$d), "với power là", tags$b(input$power), "và mức ý nghĩa", tags$b(input$sig_level), ", bạn cần một cỡ mẫu ước tính là:"),
tags$h3(style = "color: #007bff; text-align: center;", paste(res$required_n, "cho mỗi nhóm")),
tags$p(style = "text-align: center; font-style: italic;", "Tổng cỡ mẫu là ", res$required_n * 2, ".")
)
})
output$power_plot <- renderPlot({
res <- main_results()
req(res$power_plot_data)
ggplot(res$power_plot_data, aes(x = SampleSize, y = Power)) +
geom_line(color = "#007bff", size = 1.2) +
geom_hline(yintercept = input$power, linetype = "dashed", color = "red") +
geom_vline(xintercept = res$required_n, linetype = "dashed", color = "darkgreen") +
labs(title = "Power vs. Cỡ mẫu (cho mỗi nhóm)", x = "Cỡ mẫu cho mỗi nhóm (n)", y = "Power (1 - β)") +
scale_y_continuous(limits = c(0, 1)) + theme_minimal(base_size = 14)
})
# --- PHẦN PHÂN TÍCH EFFECT SIZE ---
effect_analysis_plot_data <- reactive({
req(input$sig_level, input$power, input$alternative)
effect_sizes <- seq(0.1, 1.5, by = 0.05)
required_n_values <- sapply(effect_sizes, function(d_val) {
res <- tryCatch({
pwr.t.test(
d = d_val,
sig.level = input$sig_level,
power = input$power,
type = "two.sample",
alternative = input$alternative
)$n
}, error = function(e) NA)
ceiling(res)
})
data.frame(EffectSize = effect_sizes, RequiredN = required_n_values)
})
output$effect_analysis_plot <- renderPlot({
plot_data <- effect_analysis_plot_data()
res <- main_results() # Lấy kết quả chính để đánh dấu
p <- ggplot(plot_data, aes(x = EffectSize, y = RequiredN)) +
geom_line(color = "#28a745", size = 1.2) +
geom_point(color = "#28a745", size = 3, na.rm = TRUE) +
labs(
title = paste("Cỡ mẫu cần thiết vs. Effect Size (Power cố định =", input$power, ")"),
x = "Effect Size (Cohen's d)",
y = "Cỡ mẫu cần thiết cho mỗi nhóm (n)"
) +
theme_minimal(base_size = 14)
# THÊM ĐÁNH DẤU TRỰC QUAN (IMPROVED)
if (!is.null(res) && !is.na(res$required_n)) {
p <- p +
geom_vline(xintercept = input$d, linetype = "dotted", color = "blue", size = 1) +
geom_point(aes(x = input$d, y = res$required_n), color = "blue", size = 5, shape = 18) +
annotate("text", x = input$d, y = res$required_n,
label = paste("n =", res$required_n), vjust = -1.5, color = "blue", fontface = "bold")
}
p # In đồ thị
})
}
# Chạy ứng dụng Shiny
shinyApp(ui = ui, server = server)