提问者:小点点

在闪亮的与近邻点的动态ggfield图层()


我很熟悉闪亮的基本原理,但在这里有些困难。我想能够添加一个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)
    })
  })
)

我想我从概念上理解了为什么这不起作用。绘图依赖于input$clicked,这意味着当input$clicked更改绘图时,绘图会重新渲染,但这反过来会重置input$clicked。有点像第二十二条军规。


共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而不是从函数返回对象:

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\u points,而不是调用反应函数selected()。但是,您必须确保在更改input$单击时执行renderPlot()。因此,对input$clicked的伪引用必须包含在renderPlot()中的代码中。

现在,不再需要反应性函数选择(),可以用观察()表达式替换。与reactive()相反,观察()不返回值。它只是更新全局变量selected_points每当输入$click被修改。

这种方法避免了全局变量。相反,它使用reactiveValues创建一个类似objectrv的列表,该列表具有反应式编程的特殊功能(请参见?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)
    })
  })
)

请注意,在观察者部分中,对rv的引用需要封装在隔离()中,以确保只有对输入$click的更改才会触发观察者中代码的执行>。否则,我们会得到一个无尽的循环。每当响应值rv更改时,将触发renderPlot的执行。

就个人而言,我更喜欢使用反应函数的方法1,这使得依赖关系(反应性)更加明确。我发现在方法2中对输入$clicked的伪调用不那么直观。方法3要求彻底了解反应性,并在正确的位置使用隔离()。