연속확률분포를 시각화하고 상호작용하는 Shiny 애플리케이션과 관련 코드를 설명합니다.
Shiny 앱: 사용자가 주요 연속확률분포(균등, 정규, 지수, 감마, 베타 분포) 중 하나를 선택하고 해당 분포의 매개변수를 조정할 수 있는 웹 기반 인터페이스를 제공합니다. 사용자는 선택한 분포에 따라 확률밀도함수(PDF)와 누적밀도함수(CDF)를 그래프로 시각화할 수 있습니다.
코딩: 분석 대상 데이터가 어떤 분포를 따르는지를 이해하고, 이를 그래픽으로 표현하여 데이터의 분포 형태와 경향을 확인할 수 있습니다.
continuous_dist_summary함수는 주어진 분포와 매개변수를 기반으로 요약통계량과 검정통계량, 히스토그램, 확률밀도함수(PDF)와 누적분포함수(CDF)를 계산하고 시각화합니다.
#| label: shinylive-discrete-distribution
#| viewerHeight: 700
#| standalone: true
library(shiny)
library(ggplot2)
library(bslib)
library(showtext)
tryCatch(sysfonts::font_add_google("Nanum Myeongjo", "kr"), error = function(e) NULL)
showtext_auto()
# ── Tufte 팔레트 · 테마 (자동 주입) ──────────────────────────
bit_cream <- "#fffff8"; bit_ink <- "#111111"; bit_rust <- "#8a1500"
bit_muted <- "#4a4a44"; bit_grid <- "#e8e6dc"; bit_axis <- "#8a8578"
theme_tufte_bit <- function(base_size = 13) {
ggplot2::theme_minimal(base_size = base_size) +
ggplot2::theme(
text = ggplot2::element_text(family = "kr", colour = bit_ink),
plot.title = ggplot2::element_text(family = "kr", size = base_size, hjust = 0),
plot.background = ggplot2::element_rect(fill = bit_cream, colour = NA),
panel.background = ggplot2::element_rect(fill = bit_cream, colour = NA),
panel.grid.major = ggplot2::element_line(colour = bit_grid, linewidth = 0.3),
panel.grid.minor = ggplot2::element_blank(),
axis.line = ggplot2::element_line(colour = bit_axis, linewidth = 0.3),
axis.ticks = ggplot2::element_line(colour = bit_axis, linewidth = 0.3)
)
}
if (requireNamespace("ggplot2", quietly = TRUE)) ggplot2::theme_set(theme_tufte_bit())
bit_theme <- bs_theme(
version = 5, bg = bit_cream, fg = bit_ink, primary = bit_rust,
base_font = font_collection("Palatino Linotype", "Georgia", "Times New Roman", "serif"),
"card-box-shadow" = "none"
)
# ─────────────────────────────────────────────────────────────
ui <- fluidPage(
theme = bit_theme,
titlePanel("연속형 분포"),
sidebarLayout(
sidebarPanel(
radioButtons("dist", "분포 선택:",
choices = c("균등" = "Uniform", "정규" = "Normal",
"지수" = "Exponential", "감마" = "Gamma", "베타" = "Beta"),
inline = TRUE),
conditionalPanel(
condition = "input.dist == 'Uniform'",
sliderInput("unif_min", "최솟값 (a):", min = -10, max = 0, value = -1, step = 0.1),
sliderInput("unif_max", "최댓값 (b):", min = 0, max = 10, value = 1, step = 0.1)
),
conditionalPanel(
condition = "input.dist == 'Normal'",
sliderInput("norm_mean", "평균 (μ):", min = -10, max = 10, value = 0, step = 0.1),
sliderInput("norm_sd", "표준편차 (σ):", min = 0.1, max = 5, value = 1, step = 0.1),
sliderInput("x_range_norm", "x 범위:", min = -10, max = 10, value = c(-5, 5), step = 0.1)
),
conditionalPanel(
condition = "input.dist == 'Exponential'",
sliderInput("exp_rate", "비율 (λ):", min = 0.1, max = 5, value = 1, step = 0.1),
sliderInput("x_range_exp", "x 범위:", min = 0, max = 10, value = c(0, 5), step = 0.1)
),
conditionalPanel(
condition = "input.dist == 'Gamma'",
sliderInput("gamma_shape", "형상 (α):", min = 0.1, max = 5, value = 1, step = 0.1),
sliderInput("gamma_rate", "비율 (β):", min = 0.1, max = 5, value = 1, step = 0.1),
sliderInput("x_range_gamma", "x 범위:", min = 0, max = 10, value = c(0, 5), step = 0.1)
),
conditionalPanel(
condition = "input.dist == 'Beta'",
sliderInput("beta_shape1", "형상1 (α):", min = 0.1, max = 5, value = 1, step = 0.1),
sliderInput("beta_shape2", "형상2 (β):", min = 0.1, max = 5, value = 1, step = 0.1),
sliderInput("x_range_beta", "x 범위:", min = 0, max = 1, value = c(0, 1), step = 0.01)
)
),
mainPanel(
tabsetPanel(
tabPanel("PDF 그래프", plotOutput("pdf_plot")),
tabPanel("CDF 그래프", plotOutput("cdf_plot"))
),
h4("사용 방법:"),
tags$ol(
tags$li("라디오 버튼으로 분포 종류를 선택합니다."),
tags$li("슬라이더로 선택한 분포의 모수를 조정합니다."),
tags$li("'x 범위' 슬라이더로 x축의 범위를 조절합니다."),
tags$li("각 탭에서 PDF와 CDF 그래프를 살펴봅니다.")
)
)
)
)
server <- function(input, output) {
dist_data <- reactive({
if (input$dist == "Uniform") {
x <- seq(input$unif_min-1, input$unif_max+1, length.out = 500)
data.frame(x = x, pdf = dunif(x, min = input$unif_min, max = input$unif_max),
cdf = punif(x, min = input$unif_min, max = input$unif_max))
} else if (input$dist == "Normal") {
x <- seq(input$x_range_norm[1], input$x_range_norm[2], length.out = 500)
data.frame(x = x, pdf = dnorm(x, mean = input$norm_mean, sd = input$norm_sd),
cdf = pnorm(x, mean = input$norm_mean, sd = input$norm_sd))
} else if (input$dist == "Exponential") {
x <- seq(input$x_range_exp[1], input$x_range_exp[2], length.out = 500)
data.frame(x = x, pdf = dexp(x, rate = input$exp_rate),
cdf = pexp(x, rate = input$exp_rate))
} else if (input$dist == "Gamma") {
x <- seq(input$x_range_gamma[1], input$x_range_gamma[2], length.out = 500)
data.frame(x = x, pdf = dgamma(x, shape = input$gamma_shape, rate = input$gamma_rate),
cdf = pgamma(x, shape = input$gamma_shape, rate = input$gamma_rate))
} else if (input$dist == "Beta") {
x <- seq(input$x_range_beta[1], input$x_range_beta[2], length.out = 500)
data.frame(x = x, pdf = dbeta(x, shape1 = input$beta_shape1, shape2 = input$beta_shape2),
cdf = pbeta(x, shape1 = input$beta_shape1, shape2 = input$beta_shape2))
}
})
output$pdf_plot <- renderPlot({
ggplot(dist_data(), aes(x = x, y = pdf)) +
geom_line(color = "#4a4a44") +
labs(title = "확률밀도함수 (PDF)",
x = "x", y = "밀도")
})
output$cdf_plot <- renderPlot({
ggplot(dist_data(), aes(x = x, y = cdf)) +
geom_line(color = "#4a4a44") +
labs(title = "누적분포함수 (CDF)",
x = "x", y = "누적확률")
})
}
shinyApp(ui, server)