Ch 04. 차트 클릭과 브러시 이벤트
plotly 없이도 ggplot2 차트에 인터랙션을 넣을 수 있습니다. plotOutput()은 클릭, 호버, 브러시(드래그 선택) 이벤트를 기본으로 지원합니다. 이 기능을 활용하면 차트를 클릭해 상세 정보를 보거나, 드래그로 범위를 선택해 데이터를 필터링하는 패턴을 순수 R만으로 구현할 수 있습니다.
plotOutput의 이벤트 인수
plotOutput(
outputId = "my_plot",
click = "plot_click", # 클릭 이벤트 ID
dblclick = "plot_dblclick", # 더블클릭 이벤트 ID
hover = "plot_hover", # 호버(마우스 올리기) 이벤트 ID
brush = "plot_brush" # 브러시(드래그 선택) 이벤트 ID
)
이벤트 ID로 지정한 문자열이 input$의 키가 됩니다. click = "plot_click"이면 input$plot_click으로 클릭 좌표를 읽습니다.
클릭 이벤트와 nearPoints
input$plot_click은 클릭한 위치의 x, y 좌표를 담은 리스트입니다. nearPoints()는 클릭 위치에서 가장 가까운 데이터 포인트를 찾아줍니다.
library(shiny)
library(ggplot2)
ui <- fluidPage(
titlePanel("차트 클릭으로 상세 정보 보기"),
fluidRow(
column(8,
plotOutput("scatter",
click = "point_click",
height = "400px")
),
column(4,
h4("클릭한 데이터"),
tableOutput("click_detail"),
p("차트의 점을 클릭하면 해당 차량 정보가 표시됩니다.")
)
)
)
server <- function(input, output, session) {
output$scatter <- renderPlot({
ggplot(mtcars, aes(x = hp, y = mpg, color = factor(cyl))) +
geom_point(size = 4, alpha = 0.8) +
scale_color_manual(
values = c("4" = "#2ecc71", "6" = "#3498db", "8" = "#e74c3c"),
name = "실린더"
) +
labs(title = "마력 vs 연비", x = "마력(hp)", y = "연비(mpg)") +
theme_minimal(base_size = 14)
})
output$click_detail <- renderTable({
req(input$point_click)
# 클릭 위치에서 가장 가까운 포인트 찾기
clicked <- nearPoints(
df = mtcars,
coordinfo = input$point_click,
xvar = "hp",
yvar = "mpg",
threshold = 10, # 픽셀 기준 탐색 반경
maxpoints = 1 # 최대 반환 포인트 수
)
if (nrow(clicked) == 0) return(NULL)
data.frame(
항목 = c("차종", "연비(mpg)", "마력(hp)", "실린더", "무게(wt)"),
값 = c(rownames(clicked),
clicked$mpg, clicked$hp, clicked$cyl, clicked$wt)
)
}, rownames = FALSE)
}
shinyApp(ui, server)
nearPoints()의 threshold는 픽셀 단위 탐색 반경입니다. 너무 작으면 정확히 클릭해야 하고, 너무 크면 여러 점이 잡힙니다. 10~15 픽셀이 무난합니다.
브러시 이벤트와 brushedPoints
브러시는 마우스를 드래그해서 사각형 영역을 선택하는 기능입니다. brushedPoints()는 브러시 영역 안에 있는 모든 데이터 포인트를 반환합니다.
library(shiny)
library(ggplot2)
library(dplyr)
ui <- fluidPage(
titlePanel("브러시로 범위 선택"),
fluidRow(
column(8,
p("차트에서 영역을 드래그해 데이터를 선택하세요."),
plotOutput("main_scatter",
brush = brushOpts(
id = "area_brush",
fill = "rgba(52, 152, 219, 0.2)",
stroke = "#3498db",
direction = "xy", # "x", "y", "xy" 중 선택
resetOnNew = TRUE
),
height = "400px")
),
column(4,
h4("선택된 데이터"),
textOutput("selected_count"),
tableOutput("selected_table")
)
)
)
server <- function(input, output, session) {
output$main_scatter <- renderPlot({
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(size = 3, color = "#2c3e50", alpha = 0.7) +
labs(x = "마력(hp)", y = "연비(mpg)") +
theme_minimal(base_size = 13)
})
# 브러시 영역 안의 데이터
selected <- reactive({
brushedPoints(mtcars, input$area_brush,
xvar = "hp", yvar = "mpg")
})
output$selected_count <- renderText({
n <- nrow(selected())
if (n == 0) "선택된 데이터 없음"
else paste0(n, "개 선택됨")
})
output$selected_table <- renderTable({
req(nrow(selected()) > 0)
selected()[, c("mpg", "cyl", "hp", "wt")]
}, rownames = TRUE, digits = 1)
}
shinyApp(ui, server)
brushOpts()로 브러시의 색상과 동작 방향을 지정합니다. direction = "x"이면 가로 범위만, "y"이면 세로 범위만, "xy"이면 사각형 영역을 선택합니다.
선택 강조: 브러시로 선택된 점 색상 변경
가장 실용적인 패턴은 브러시로 선택된 점을 색상으로 구분하고, 선택 결과를 테이블에 보여주는 것입니다.
library(shiny)
library(ggplot2)
ui <- fluidPage(
plotOutput("linked_scatter",
brush = "region_brush",
height = "400px"),
tableOutput("region_table")
)
server <- function(input, output, session) {
output$linked_scatter <- renderPlot({
# 브러시 영역 안의 점을 강조
in_brush <- brushedPoints(mtcars, input$region_brush,
xvar = "hp", yvar = "mpg",
allRows = TRUE) # 전체 행 반환
ggplot(in_brush, aes(x = hp, y = mpg,
color = selected_, # 선택 여부 열
size = selected_)) +
geom_point(alpha = 0.8) +
scale_color_manual(
values = c("TRUE" = "#e74c3c", "FALSE" = "#95a5a6"),
guide = "none"
) +
scale_size_manual(
values = c("TRUE" = 5, "FALSE" = 2),
guide = "none"
) +
labs(x = "마력(hp)", y = "연비(mpg)") +
theme_minimal()
})
output$region_table <- renderTable({
brushedPoints(mtcars, input$region_brush,
xvar = "hp", yvar = "mpg")
}, rownames = TRUE, digits = 1)
}
shinyApp(ui, server)
brushedPoints(allRows = TRUE)는 선택 여부를 나타내는 selected_ 열이 추가된 전체 데이터프레임을 반환합니다. 이 열을 aes(color = selected_)로 활용해 선택된 점과 그렇지 않은 점을 다르게 표시합니다.
호버 이벤트와 nearPoints
마우스를 올렸을 때 말풍선처럼 정보를 보여주는 패턴입니다.
library(shiny)
library(ggplot2)
ui <- fluidPage(
plotOutput("hover_scatter",
hover = hoverOpts(
id = "plot_hover",
delay = 100, # 호버 반응 지연(ms)
delayType = "debounce" # 또는 "throttle"
),
height = "400px"),
verbatimTextOutput("hover_info")
)
server <- function(input, output, session) {
output$hover_scatter <- renderPlot({
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(size = 3, color = "#2980b9", alpha = 0.7) +
theme_minimal()
})
output$hover_info <- renderPrint({
point <- nearPoints(mtcars, input$plot_hover,
xvar = "hp", yvar = "mpg",
threshold = 8, maxpoints = 1)
if (nrow(point) == 0) {
cat("그래프 위에 마우스를 올려보세요.\n")
} else {
cat("차종:", rownames(point), "\n")
cat("마력:", point$hp, "hp\n")
cat("연비:", point$mpg, "mpg\n")
}
})
}
shinyApp(ui, server)
세 가지 이벤트 비교
| 이벤트 | 함수 | 반환 | 주용도 |
|---|---|---|---|
click |
nearPoints() |
클릭 근처 행 | 선택 후 상세 정보 조회 |
brush |
brushedPoints() |
선택 영역 내 행 | 범위 선택으로 필터링 |
hover |
nearPoints() |
호버 근처 행 | 마우스 오버 툴팁 |
클릭은 명시적 선택, 브러시는 범위 선택, 호버는 탐색에 적합합니다. 세 가지를 조합하면 드래그로 범위를 좁히고, 클릭으로 하나를 고르고, 호버로 미리 보는 대화형 탐색 환경을 완성할 수 있습니다.