#| label: shinylive-prob-law
#| viewerHeight: 700
#| standalone: true
library(shiny)
library(ggplot2)
library(dplyr)
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("CSV 데이터 시각화"),
sidebarLayout(
sidebarPanel(
fileInput("file", "CSV 파일 선택",
accept = c("text/csv", "text/comma-separated-values,text/plain", ".csv")),
selectInput("x_var", "X축 변수", choices = NULL),
selectInput("y_var", "Y축 변수", choices = NULL),
selectInput("color_var", "색상 변수", choices = NULL),
checkboxInput("reg_line", "회귀선 추가", value = FALSE),
sliderInput("alpha", "점 투명도", min = 0, max = 1, value = 0.5, step = 0.1),
downloadButton("download_plot", "그래프 저장")
),
mainPanel(
plotOutput("plot"),
br(),
h4("사용 방법:"),
tags$ol(
tags$li("'CSV 파일 선택' 버튼으로 CSV 파일을 업로드합니다."),
tags$li("드롭다운에서 X축·Y축·색상(선택) 변수를 고릅니다."),
tags$li("회귀선을 추가하거나 점 투명도를 조절해 그래프를 꾸밉니다."),
tags$li("선택에 따라 그래프가 자동으로 갱신됩니다."),
tags$li("'그래프 저장' 버튼으로 그래프를 PNG로 저장합니다.")
)
)
)
)
server <- function(input, output, session) {
data <- reactive({
req(input$file)
read.csv(input$file$datapath)
})
observeEvent(data(), {
updateSelectInput(session, "x_var", choices = names(data()))
updateSelectInput(session, "y_var", choices = names(data()))
updateSelectInput(session, "color_var", choices = c("None", names(data())))
})
output$plot <- renderPlot({
req(input$x_var, input$y_var)
p <- ggplot(data(), aes_string(x = input$x_var, y = input$y_var)) +
geom_point(alpha = input$alpha)
if (input$color_var != "None") {
p <- p + aes_string(color = input$color_var)
}
if (input$reg_line) {
p <- p + geom_smooth(method = "lm")
}
p
})
output$download_plot <- downloadHandler(
filename = function() {
paste("plot", Sys.Date(), ".png", sep = "_")
},
content = function(file) {
ggsave(file, plot = last_plot(), device = "png", width = 8, height = 6, dpi = 300)
}
)
}
shinyApp(ui, server)