请试试这个:
方法 1(推荐)
library(shiny)
library(ggplot2)
# initialize global variable to record selected (clicked) rows
selected_points <- mtcars[0, ]
str(selected_points)
shinyApp(
ui = shinyUI(
plotOutput("plot", click = "clicked")
),
server = shinyServer(function(input, output) {
selected <- reactive({
# add clicked
selected_points <<- rbind(selected_points, nearPoints(mtcars, input$clicked))
# remove _all_ duplicates if any (toggle mode)
# http://stackoverflow.com/a/13763299/3817004
selected_points <<-
selected_points[!(duplicated(selected_points) |
duplicated(selected_points, fromLast = TRUE)), ]
str(selected_points)
return(selected_points)
})
output$plot <- renderPlot({
ggplot(mtcars, aes(x = mpg, y = wt)) +
geom_point() +
geom_point(data = selected(), colour = "red", size = 5)
})
})
)
如果您单击一个点,它就会突出显示。如果您再次单击它,突出显示将再次关闭(切换)。
代码使用全局变量 selected_points 来存储实际突出显示(选定)的点,并使用响应式表达式 selected() 在单击点时更新全局变量。
str(selected_points) 可能有助于可视化工作,但可以删除。
方法 2(替代)
有一种稍微不同的方法,它使用observe() 而不是reactive() 并直接引用全局变量selected_points 而不是从函数中返回对象:
library(shiny)
library(ggplot2)
selected_points <- mtcars[0, ]
str(selected_points)
shinyApp(
ui = shinyUI(
plotOutput("plot", click = "clicked")
),
server = shinyServer(function(input, output) {
observe({
# add clicked
selected_points <<- rbind(selected_points, nearPoints(mtcars, input$clicked))
# remove _all_ duplicates (toggle)
# http://stackoverflow.com/a/13763299/3817004
selected_points <<-
selected_points[!(duplicated(selected_points) |
duplicated(selected_points, fromLast = TRUE)), ]
str(selected_points)
})
output$plot <- renderPlot({
# next statement is required for reactivity
input$clicked
ggplot(mtcars, aes(x = mpg, y = wt)) +
geom_point() +
geom_point(data = selected_points, colour = "red", size = 5)
})
})
)
当然,你可以直接在ggplot调用中使用全局变量selected_points,而不是调用反应函数selected()。但是,您必须确保在更改 input$clicked 时执行 renderPlot()。因此,input$clicked 的虚拟引用必须包含在 renderPlot() 内的代码中。
现在,反应函数 selected() 不再需要,可以替换为 observe() 表达式。与reactive() 不同,observe() 不返回值。只要修改了input$clicked,它就会更新全局变量selected_points。
方法 3(反应值)
这种方法避免了全局变量。相反,它使用reactiveValues 创建一个类似列表的对象rv,具有响应式编程的特殊功能(请参阅?reactiveValues)。
library(shiny)
library(ggplot2)
shinyApp(
ui = shinyUI(
plotOutput("plot", click = "clicked")
),
server = shinyServer(function(input, output) {
rv <- reactiveValues(selected_points = mtcars[0, ])
observe({
# add clicked
rv$selected_points <- rbind(isolate(rv$selected_points),
nearPoints(mtcars, input$clicked))
# remove _all_ duplicates (toggle)
# http://stackoverflow.com/a/13763299/3817004
rv$selected_points <- isolate(
rv$selected_points[!(duplicated(rv$selected_points) |
duplicated(rv$selected_points, fromLast = TRUE)), ])
str(rv$selected_points)
})
output$plot <- renderPlot({
ggplot(mtcars, aes(x = mpg, y = wt)) +
geom_point() +
geom_point(data = rv$selected_points, colour = "red", size = 5)
})
})
)
请注意,observer 部分中对rv 的引用需要封装在isolate() 中,以确保只有对input$clicked 的更改才会触发observer 中代码的执行。否则,我们将陷入无限循环。每当反应值rv 发生变化时,都会触发renderPlot 的执行。
结论
就个人而言,我更喜欢使用反应函数的方法 1,它使依赖关系(反应性)更加明确。我发现在方法 2 中对 input$clicked 的虚拟调用不太直观。方法 3 需要彻底了解反应性并在正确的位置使用 isolate()。