使用 R shiny 集成时间序列图和传单图

Integrating time series graphs and leaflet maps using R shiny

我有 data/results,其中包含一个地理编码位置 (latitude/longitude) 和一个 date/time 标记,我想使用 R shiny 与之交互。我创建了 R shiny 应用程序,其中包含多个传单地图(传单 R 包),还包含时间序列图(dygraphs R 包)。我知道如何同步不同的 dygraphs (https://rstudio.github.io/dygraphs/gallery-synchronization.html),但也不确定如何将它同步到传单地图。我的问题是如何最好地将所有图表 link 放在一起,所以当我 select 传单地图上的一个区域或 dygraph 时间序列图上的一段时间时,其他图表都会更新以仅显示过滤数据?

有人认为我曾经使用过传单插件,但不确定如何使用 R/shiny 做到这一点?例如,我看到一些传单插件提供了为包含 date/time 信息 (http://apps.socib.es/Leaflet.TimeDimension/examples/) 的地图制作动画的功能。另一个问题是 documentation/examples 展示了如何使用 R shiny 使用传单插件吗?

我认为可以从时间序列图 (dygraph) 中提取 selected 的 time/date,但不确定 if/how 提取显示的区域在 R shiny 的传单地图上。我的最后一个问题是,是否可以提取显示传单地图的区域,以便更新时间序列图。

提前感谢您就如何使用 R shiny 将传单图与时间序列图(即 dygraph)相结合提出任何建议!

这可能是一个连续的讨论,而不是一个单一的答案。

幸运的是,您的问题涉及由 RStudio 创建的 htmlwidgets,RStudio 还制作了 Shiny。他们付出了额外的努力将 Shiny 通信集成到 dygraphsleaflet 中。许多其他 htmlwidgets 并非如此。为了更广泛地讨论 Shiny 之外的内部 htmlwidget 通信,我建议关注 this Github issue.

第 1 部分 - 传单控制 dygraph

作为我的第一个示例,我们将让 leaflet 控制 dygraphs,因此单击墨西哥的一个州会将 dygraph 绘图限制为该州。我应该相信这三个例子。

  1. Kyle Walker's Rpub Mexico Choropleth Leaflet
  2. Shiny example included in leaflet
  3. Diego Valle Crime in Mexico project

R代码

  # one piece of an answer to this Whosebug question
  #  

  # for this we'll use Kyle Walker's rpubs example
  #   http://rpubs.com/walkerke/leaflet_choropleth
  # combined with data from Diego Valle's crime in Mexico project
  #   https://github.com/diegovalle/mxmortalitydb

  # we'll also build on the shiny example included in leaflet
  #  https://github.com/rstudio/leaflet/blob/master/inst/examples/shiny.R

  library(shiny)
  library(leaflet)
  library(dygraphs)
  library(rgdal)

  # let's build this in advance so we don't download the
  #    data every time
  tmp <- tempdir()
  url <- "http://personal.tcu.edu/kylewalker/data/mexico.zip"
  file <- basename(url)
  download.file(url, file)
  unzip(file, exdir = tmp)
  mexico <- {
    readOGR(dsn = tmp, layer = "mexico", encoding = "UTF-8")
    #delete our files since no longer need
    on.exit({unlink(tmp);unlink(file)})
  }
  pal <- colorQuantile("YlGn", NULL, n = 5)

  leaf_mexico <- leaflet(data = mexico) %>%
    addTiles() %>%
    addPolygons(fillColor = ~pal(gdp08), 
                fillOpacity = 0.8, 
                color = "#BDBDC3", 
                weight = 1,
                layerId = ~id)

  # now let's get our time series data from Diego Valle
  crime_mexico <- jsonlite::fromJSON(
    "https://rawgit.com/diegovalle/crimenmexico.diegovalle.net/master/assets/json/states.json"
  )

  ui <- fluidPage(
    leafletOutput("map1"),
    dygraphOutput("dygraph1",height = 200),
    textOutput("message", container = h3)
  )

  server <- function(input, output, session) {
    v <- reactiveValues(msg = "")

    output$map1 <- renderLeaflet({
      leaf_mexico
    })

    output$dygraph1 <- renderDygraph({
      # start dygraph with all the states
      crime_wide <- reshape(
        crime_mexico$hd[,c("date","rate","state_code"),drop=F],
        v.names="rate",
        idvar = "date",
        timevar="state_code",
        direction="wide"
      )
      colnames(crime_wide) <- c("date",as.character(mexico$state))
      rownames(crime_wide) <- as.Date(crime_wide$date)
      dygraph(
        crime_wide[,-1]
      )
    })

    observeEvent(input$map1_shape_mouseover, {
      v$msg <- paste("Mouse is over shape", input$map1_shape_mouseover$id)
    })
    observeEvent(input$map1_shape_mouseout, {
      v$msg <- ""
    })
    observeEvent(input$map1_shape_click, {
      v$msg <- paste("Clicked shape", input$map1_shape_click$id)
      #  on our click let's update the dygraph to only show
      #    the time series for the clicked
      state_crime_data <- subset(crime_mexico$hd,state_code == input$map1_shape_click$id)
      rownames(state_crime_data) <- as.Date(state_crime_data$date)
      output$dygraph1 <- renderDygraph({
        dygraph(
          xts::as.xts(state_crime_data[,"rate",drop=F]),
          ylab = paste0(
            "homicide rate ",
            as.character(mexico$state[input$map1_shape_click$id])
          )
        )
      })
    })
    observeEvent(input$map1_zoom, {
      v$msg <- paste("Zoom changed to", input$map1_zoom)
    })
    observeEvent(input$map1_bounds, {
      v$msg <- paste("Bounds changed to", paste(input$map1_bounds, collapse = ", "))
    })

    output$message <- renderText(v$msg)
  }

  shinyApp(ui, server)

第 2 部分 dygraph 控制传单 + 第 1 部分传单控制 dygraph

# one piece of an answer to this Whosebug question
#  

# for this we'll use Kyle Walker's rpubs example
#   http://rpubs.com/walkerke/leaflet_choropleth
# combined with data from Diego Valle's crime in Mexico project
#   https://github.com/diegovalle/mxmortalitydb

# we'll also build on the shiny example included in dygraphs
#  https://github.com/rstudio/leaflet/blob/master/inst/examples/shiny.R

library(shiny)
library(leaflet)
library(dygraphs)
library(dplyr)
library(rgdal)

# let's build this in advance so we don't download the
#    data every time
tmp <- tempdir()
url <- "http://personal.tcu.edu/kylewalker/data/mexico.zip"
file <- basename(url)
download.file(url, file)
unzip(file, exdir = tmp)
mexico <- {
  #delete our files since no longer need
  on.exit({unlink(tmp);unlink(file)})  
  readOGR(dsn = tmp, layer = "mexico", encoding = "UTF-8")
}

# now let's get our time series data from Diego Valle
crime_mexico <- jsonlite::fromJSON(
  "https://rawgit.com/diegovalle/crimenmexico.diegovalle.net/master/assets/json/states.json"
)

# instead of the gdp data, let's use mean homicide_rate
#   for our choropleth
mexico$homicide <- crime_mexico$hd %>%
  group_by( state_code ) %>%
  summarise( homicide = mean(rate) ) %>%
  ungroup() %>%
  select( homicide ) %>%
  unlist


pal <- colorBin(
  palette = RColorBrewer::brewer.pal(n=9,"YlGn")[-(1:2)]
  , domain = c(0,50)
  , bins =7
)

popup <- paste0("<strong>Estado: </strong>", 
                      mexico$name, 
                      "<br><strong>Homicide Rate: </strong>", 
                      round(mexico$homicide,2)
          )

leaf_mexico <- leaflet(data = mexico) %>%
  addTiles() %>%
  addPolygons(fillColor = ~pal(homicide), 
              fillOpacity = 0.8, 
              color = "#BDBDC3", 
              weight = 1,
              layerId = ~id,
              popup = popup
              )


ui <- fluidPage(
  leafletOutput("map1"),
  dygraphOutput("dygraph1",height = 200),
  textOutput("message", container = h3)
)

server <- function(input, output, session) {
  v <- reactiveValues(msg = "")

  output$map1 <- renderLeaflet({
    leaf_mexico
  })

  output$dygraph1 <- renderDygraph({
    # start dygraph with all the states
    crime_wide <- reshape(
      crime_mexico$hd[,c("date","rate","state_code"),drop=F],
      v.names="rate",
      idvar = "date",
      timevar="state_code",
      direction="wide"
    )
    colnames(crime_wide) <- c("date",as.character(mexico$state))
    rownames(crime_wide) <- as.Date(crime_wide$date)
    dygraph( crime_wide[,-1])  %>%
      dyLegend( show = "never" )
  })

  observeEvent(input$dygraph1_date_window, {
    if(!is.null(input$dygraph1_date_window)){
      # get the new mean based on the range selected by dygraph
      mexico$filtered_rate <- crime_mexico$hd %>%
      filter( 
              as.Date(date) >= as.Date(input$dygraph1_date_window[[1]]),
              as.Date(date) <= as.Date(input$dygraph1_date_window[[2]])  
            ) %>%
      group_by( state_code ) %>%
      summarise( homicide = mean(rate) ) %>%
      ungroup() %>%
      select( homicide ) %>%
      unlist

      # leaflet comes with this nice feature leafletProxy
      #  to avoid rebuilding the whole map
      #  let's use it
      leafletProxy( "map1", data = mexico  ) %>%
        removeShape( layerId = ~id ) %>%
        addPolygons( fillColor = ~pal( filtered_rate ), 
                    fillOpacity = 0.8, 
                    color = "#BDBDC3", 
                    weight = 1,
                    layerId = ~id,
                    popup = paste0("<strong>Estado: </strong>", 
                        mexico$name, 
                        "<br><strong>Homicide Rate: </strong>", 
                        round(mexico$filtered_rate,2)
                    )
                    )
    }
  })

  observeEvent(input$map1_shape_click, {
    v$msg <- paste("Clicked shape", input$map1_shape_click$id)
    #  on our click let's update the dygraph to only show
    #    the time series for the clicked
    state_crime_data <- subset(crime_mexico$hd,state_code == input$map1_shape_click$id)
    rownames(state_crime_data) <- as.Date(state_crime_data$date)
    output$dygraph1 <- renderDygraph({
      dygraph(
        xts::as.xts(state_crime_data[,"rate",drop=F]),
        ylab = paste0(
          "homicide rate ",
          as.character(mexico$state[input$map1_shape_click$id])
        )
      )
    })
  })

}

shinyApp(ui, server)