带有nearPoints()的动态ggplot图层

rom*_*man 11 r reactive-programming ggplot2 shiny

我熟悉闪亮的基础知识但在这里挣扎.我希望能够在点击一个点时添加ggplot图层以突出显示该点.我知道这是可能的ggvis和画廊中有一个很好的例子,但我希望能够nearPoints()用来捕获点击作为ui输入.

我尝试了一些东西(见下文),它与ggplot图层分开显示然后消失.我已尝试过各种编辑reactive(),eventReactive()依此类推.

任何帮助深表感谢...

library(shiny)
library(ggplot2)

shinyApp(
  ui = shinyUI(
        plotOutput("plot", click = "clicked")
    ),

  server = shinyServer(function(input, output) {
    output$plot <- renderPlot({
      ggplot(mtcars, aes(x = mpg, y = wt)) +
        geom_point() +
        geom_point(data = nearPoints(mtcars, input$clicked), colour = "red", size = 5)
    })
  })
)
Run Code Online (Sandbox Code Playgroud)

我想我从概念上理解为什么这不起作用.该图具有依赖性,input$clicked这意味着当input$clicked更改时,图重新渲染,但这又会重置input$clicked.有点抓人22的情况.

Uwe*_*Uwe 13

请试试这个:

方法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)
    })
  })
)
Run Code Online (Sandbox Code Playgroud)

如果单击某个点,则会突出显示该点.如果再次单击它,则再次关闭突出显示(切换).

代码使用全局变量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)
    })
  })
)
Run Code Online (Sandbox Code Playgroud)

当然,您可以selected_points直接在ggplot调用中使用全局变量,而不是调用反应函数selected().但是,您必须确保renderPlot()input$clicked更改时执行.因此,虚拟引用input$clicked必须包含在代码中renderPlot().

现在,selected()不再需要反应函数,可以用observe()表达式替换.相反reactive(),observe()不返回值.它只是selected_pointsinput$clicked修改时更新全局变量.

方法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)
    })
  })
)
Run Code Online (Sandbox Code Playgroud)

请注意,在observer部分引用中rv需要封装isolate()以确保只有更改input$clicked才会触发代码的执行observer.否则,我们将获得无限循环.renderPlot只要反应值rv发生变化,就会触发执行.

结论

就个人而言,我更喜欢使用反应函数的方法1,这使得依赖性(反应性)更加明确.我发现在方法2中输入$ click的虚拟调用不太直观.方法3需要彻底了解反应性并isolate()在正确的地方使用.