如何使动作按钮eventReactive()到Shiny中的多个滑动条

时间:2018-01-30 20:43:51

标签: r shiny leaflet r-leaflet

我试图在用户点击操作按钮后使用滑动条值进行闪亮计算。这些滑动条代表五个维度,用户可以拖动这些维度来设置每个变量的首选权重值。单击操作按钮后,应用程序应显示荷兰地图以及基于这些权重值具有最高“得分”的城市。目前我已经运行了以下代码,但是在您单击“操作”按钮后非常缓慢,因为它会在反应性滑动条更改时自动重新执行。是否有人建议让我的代码更快,并且操作按钮eventReactive以便必须单击它以让闪亮知道进行新的计算?

我已经在主要脚本中添加了一个gist链接,因为我想不出更简单的方式来共享我的数据(spatialpolygonsdataframe):https://gist.github.com/anonymous/602948bce91e61f36fd3eb0c4259d26c

Shiny app的屏幕截图:https://i.stack.imgur.com/k0Vg9.png

app.R

library(shiny)
library(leaflet)

ui <- fluidPage(
  titlePanel("Quality-of-life-o-meter of The Netherlands"),

  sidebarLayout(
    sidebarPanel(
      h3("Dimensions"),
      h6("Assign a weight value for every dimension and press the 'find!' button"),
      sliderInput("housingslider", 
                  label = h4("Housing"), 
                  min = 1, 
                  max = 5, 
                  value = 1),
      sliderInput("populationslider", 
                  label = h4("Population"), 
                  min = 1, 
                  max = 5, 
                  value = 1),
      sliderInput("provisionsslider", 
                  label = h4("Provisions"), 
                  min = 1, 
                  max = 5, 
                  value = 1),
      sliderInput("safetyslider", 
                  label = h4("Safety"), 
                  min = 1, 
                  max = 5, 
                  value = 1),
      sliderInput("physicalenvslider", 
                  label = h4("Physical Environment"), 
                  min = 1, 
                  max = 5, 
                  value = 1),
      actionButton("action", label = "Find!")
    ),
    mainPanel(
      leafletOutput("mymap", height = "800")
    )
  )
)

server <- function(input, output, session) {

  output$mymap <-renderLeaflet({
    # Create interactive map of Total Score of Muncipalities in 2016 to display first
    leaflet(data = MunScores2016) %>% addTiles() %>%
      addPolygons(fillColor = ~pal(Total_Score_2016), 
                  fillOpacity = 1, 
                  color = 'white', 
                  weight = 1,
                  popup = popup_dat) %>%
      addLegend("bottomright", # Legend position
                pal=pal, # color palette
                values=~Total_Score_2016, # legend values
                opacity = 0.7,
                title="Percentage difference from national average")
  })  

  observeEvent(input$action,
               output$mymap <-renderLeaflet({
                 Total_Score <- NA
                 Total_Score <- ((input$housingslider * MunScores2016$Housing_Score_2016 +
                                input$populationslider * MunScores2016$Population_Score_2016 +
                                input$provisionsslider * MunScores2016$Provisions_Score_2016 +
                                input$safetyslider * MunScores2016$Safety_Score_2016 +
                                input$physicalenvslider * MunScores2016$PhysicalEnvironment_Score_2016)/
                               (input$housingslider + input$populationslider + input$provisionsslider + input$safetyslider + input$physicalenvslider))
             #Create interactive map
                 leaflet(data = MunScores2016) %>% addTiles() %>%
                   addPolygons(fillColor = ~pal(Total_Score), 
                               fillOpacity = 1, 
                               color = 'white', 
                               weight = 1,
                               popup = paste0("<strong>Municipality:</strong>", 
                                          MunScores2016$Municipality_Name, 
                                          "<br><strong>Quality-of-life-o-meter says: </strong>", 
                                          Total_Score)) %>%
                   addLegend("bottomright", # Legend position
                             pal=pal, # color palette
                             values=~Total_Score_2016, # legend values
                             opacity = 0.7,
                             title="Weighted percentage difference from national average")
               })

  )
}  
shinyApp(ui = ui, server = server)

1 个答案:

答案 0 :(得分:1)

您的代码很慢,因为您在不知情的情况下不断重新创建所有内容。

刷新

让我先解决滑块刷新问题:

observeEvent内(实际上只有在按下按钮时才触发),您将反应环境分配给output$mymap(所有渲染功能都是反应环境)。 / p>

renderLeaflet内,然后命名输入参数,Shiny将自动跟踪这些输入作为可观察对象

因此,一旦单击按钮,渲染函数的赋值就会发生,但渲染函数本身不仅限于此事件,因为反应环境比分配电话更长寿。

有两种方法可以将此逻辑从渲染函数中拉出来。

1)在每个输入参考周围使用isolate。 (即isolate(input$housingslider))。这样你抑制到输入滑块的反应链接,渲染可观察的将不再有更新触发器,只在你创建时更新一次。

output$mymap <-renderLeaflet({
  Total_Score <- NA
  # isolate(expr) prevents observables in expr from being subscribed to.
  Total_Score <- isolate(
    (
      input$housingslider * MunScores2016$Housing_Score_2016
      + input$populationslider * MunScores2016$Population_Score_2016  
      + input$provisionsslider * MunScores2016$Provisions_Score_2016 
      + input$safetyslider * MunScores2016$Safety_Score_2016
      + input$physicalenvslider * PhysicalEnvironment_Score_2016
    ) / (
      input$housingslider
      + input$populationslider
      + input$provisionsslider 
      +  input$safetyslider 
      + input$physicalenvslider
    )
  )

  ...
})

2)更好:将Total_Score 的计算移到renderLeaflet函数的之外。这样,输入的可观察对象在被动环境中没有命名,即renderLeaflet,它只知道它无法订阅的值Total_Score。 。输入的observable只属于外部observeEvent,它将忽略除操作按钮点击之外的输入更改。

observeEvent(input$action, {
  Total_Score <- NA
  # isolate(expr) prevents observables in expr from being subscribed to.
  Total_Score <- (
    input$housingslider * MunScores2016$Housing_Score_2016
    + input$populationslider * MunScores2016$Population_Score_2016  
    + input$provisionsslider * MunScores2016$Provisions_Score_2016 
    + input$safetyslider * MunScores2016$Safety_Score_2016
    + input$physicalenvslider * PhysicalEnvironment_Score_2016
  ) / (
    input$housingslider
    + input$populationslider
    + input$provisionsslider 
    +  input$safetyslider 
    + input$physicalenvslider
  )

  output$mymap <-renderLeaflet({
    # Use Total_Score here and no input$
    ...
  })
})

重绘

但是现在你实际应该实现这个:使用leafletProxy !!

每次调用renderLeaflet时,都会销毁之前使用过的地图对象,并创建一个全新的地图对象(并再次创建所有图块,这也意味着获取这些资源)。只要看到每次都重置缩放和查看位置,您就会意识到这一点。

有一种方法可以重复使用之前创建的地图。

函数leafletProxy(<name>)可让您访问使用output$<name> <- renderLeaflet(...)创建的地图,并可访问常规传单调用的所有功能。

因此,我们将代码重写为在开始时只有一个渲染函数,而不再进行重新创建。服务器功能如下:

server <- function(input, output, session) {

  # Create Map once.
  output$mymap <-renderLeaflet({
    leaflet(data = MunScores2016)
      %>% addTiles()
      %>% addPolygons(
        fillColor = ~pal(Total_Score_2016),
        fill2Opacity = 1, 
        color = 'white', 
        weight = 1,
        popup = popup_dat)
      %>% addLegend(
        "bottomright",
        pal=pal,
        values=~Total_Score_2016,
        opacity = 0.7,
        title="Percentage difference from national average")
  })

  observeEvent(input$action, {
    Total_Score <- NA
    Total_Score <- ((input$housingslider * MunScores2016$Housing_Score_2016 +
      input$populationslider * MunScores2016$Population_Score_2016 +
      input$provisionsslider * MunScores2016$Provisions_Score_2016 +
      input$safetyslider * MunScores2016$Safety_Score_2016 +
      input$physicalenvslider * MunScores2016$PhysicalEnvironment_Score_2016)/
      (input$housingslider + input$populationslider + input$provisionsslider + input$safetyslider + input$physicalenvslider))

    # Just update the existing map
    leafletProxy("mymap")
      # This you have to do now: remove the existing polygons, before you paint new ones.
      %>% clearShapes()
      %>% addPolygons(
        fillColor = ~pal(Total_Score), 
        fillOpacity = 1, 
        color = 'white', 
        weight = 1,
        popup = paste0("<strong>Municipality:</strong>", 
          MunScores2016$Municipality_Name, 
          "<br><strong>Quality-of-life-o-meter says: </strong>", 
          Total_Score))
      # As you can see, no addLegend is needed, since the legend does not change. 
  })
}

使用此功能,上述所有刷新问题都不再发生,因为您不会为地图重新创建反应环境。不过,我认为第一部分将来会有所帮助。