R按月(当前按天)动态日期滑块动画

时间:2016-11-03 02:02:11

标签: r shiny

我对R感觉有点舒服,很少用Shiny,虽然它不是我的第一个Shiny应用程序。

我有一个带有lon / lat的数据框,以及每个新客户在系统中输入的日期/时间。我还根据startDate变量创建了其他变量,如年,月,周,年 - 月(ym)和年 - 周(yw):

  id      lat       lon  startDate year month week         ym         yw
1  1 45.53814 -73.63672 2014-04-09 2014     4   15 2014-04-01 2014-04-06
2  2 45.51076 -73.61029 2014-06-04 2014     6   23 2014-06-01 2014-06-01
3  3 45.43560 -73.60100 2014-04-30 2014     4   18 2014-04-01 2014-04-27
4  4 45.54332 -73.56000 2014-05-30 2014     5   22 2014-05-01 2014-05-25
5  5 45.52234 -73.59022 2014-05-01 2014     5   18 2014-05-01 2014-04-27

我想用传单映射每个客户(这已经完成),但是我想通过仅显示特定日期范围内的新客户来动画我的应用程序。

我想逐步完成月度日期(ym变量:2016-01-01,2016-02-01,2016-03-01 ...)而不是白天(或已经支持的x天)因为月度日期并不总是朝向下个月的30天。 这是我目前的申请:

library(shiny)
library(leaflet)
library(dplyr)

df <- data.frame(id = 1:5, 
             lat = c(45.53814, 45.51076, 45.4356, 45.54332, 45.52234), 
             lon = c(-73.63672, -73.61029, -73.6010, -73.56000, -73.59022),
             startDate = as.Date(c("2014-04-09", "2014-06-04", "2014-04-30", "2014-05-30", "2014-05-01")),
             year = c(2014, 2014, 2014, 2014, 2014),
             month = c(4, 6, 4, 5, 5),
             week = c(15, 23, 18, 22, 18),
             ym = as.Date(c("2014-04-01", "2014-06-01", "2014-04-01", "2014-05-01", "2014-05-01")),  # Year-Month
             yw = as.Date(c("2014-04-06", "2014-06-01", "2014-04-27", "2014-05-25", "2014-04-27"))   # Year-Week
             )


ui <- bootstrapPage(
  tags$style(type = "text/css", "html, body {width:100%;height:100%}"),

  leafletOutput("map", width = "83%", height = "100%"),

  absolutePanel(
top = 1,
right = 10,

div(
  style = "height: 80px;",
  sliderInput(
    "time",
    "Time Slider",
    min(df$month),
    max(df$month),
    value = c(min(df$month), max(df$month)),
    step = 1,
    animate = animationOptions(interval = 2500)

  ) # end sliderInput
) # end div
  ) # end absolutePanel
) # end bootstrapPage

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

  output$map <- renderLeaflet({
    leaflet(data = df %>% filter(month >= input$time[1], month <=             input$time[2])) %>% addTiles() %>% 
  addMarkers(~lon, ~lat) %>% 
  setView(lng = -73.6, lat = 45.52, zoom = 12)
    })
  })
shinyApp(ui = ui, server = server)

问题:如何使用滑块动画选项过滤数据以转移到下个月,依此类推?现在我循环变量月份,但我有8年的数据,所以我也需要考虑年份,因此循环通过ym变量。

我看到一些工作已完成herehere,但要么它没有响应我的需求,要么我不理解提供的js代码。如果是这样,需要如何更改我的代码以反映我的需求?

谢谢。

2 个答案:

答案 0 :(得分:12)

编辑2017-10-13:此功能现在可以在包shinyWidgets中使用(名称不同:sliderTextInput())。

您可以使用此自定义滑块功能。它需要一个字符向量进行选择,因此您可以使用任何您想要的格式并逐步完成选择。缺点是您必须手动拆分服务器中的输入:

app示例:

# List of months
choices_month <- format(seq.Date(from = as.Date("2014-01-01"), by = "month", length.out = 36), "%B-%Y")

library("shiny")

# ui
ui <- fluidPage(
  br(),

  # custom slider function
  sliderValues(
    inputId = "test", label = "Month", width = "100%",
    values = choices_month, 
    from = choices_month[2], to = choices_month[6],
    grid = FALSE, animate = animationOptions(interval = 1500)
  ),
  verbatimTextOutput("res")
)

# server
server <- function(input, output, session) {
  output$res <- renderPrint({
    print(input$test) # you have to split manually the result by ";"
    print(as.Date(paste("01", unlist(strsplit(input$test, ";")), sep="-"), format="%d-%B-%Y"))
  })
}

# App
shinyApp(ui = ui, server = server)

sliderValues函数:

sliderValues <- function (inputId,
                          label,
                          values,
                          from,
                          to = NULL,
                          grid = TRUE,
                          width = NULL,
                          postfix = NULL,
                          prefix = NULL,
                          dragRange = TRUE,
                          disable = FALSE,
                          animate = FALSE) {
  validate_fromto <-
    function(fromto = NULL,
             values = NULL,
             default = 0) {
      if (!is.null(fromto)) {
        if (is.character(values) & is.numeric(fromto)) {
          fromto <- fromto - 1
        } else {
          fromto <- which(values == fromto) - 1
        }
      } else {
        fromto <- default
      }
      return(fromto)
    }

  sliderProps <- shiny:::dropNulls(
    list(
      class = "js-range-slider",
      id = inputId,
      `data-type` = if (!is.null(to))
        "double"
      else
        "single",
      `data-from` = validate_fromto(fromto = from, values = values),
      `data-to` = validate_fromto(
        fromto = to,
        values = values,
        default = length(values)
      ),
      `data-grid` = grid,
      `data-prefix` = if (is.null(prefix)) {
        "null"
      } else {
        shQuote(prefix, "sh")
      },
      `data-postfix` = if (is.null(postfix)) {
        "null"
      } else {
        shQuote(postfix, "sh")
      },
      `data-drag-interval` = dragRange,
      `data-disable` = disable,
      `data-values` = if (is.numeric(values)) {
        paste(values, collapse = ", ")
      } else {
        paste(shQuote(values, type = "sh"), collapse = ", ")
      }
    )
  )
  sliderProps <- lapply(
    X = sliderProps,
    FUN = function(x) {
      if (identical(x, TRUE))
        "true"
      else if (identical(x, FALSE))
        "false"
      else
        x
    }
  )
  sliderTag <- tags$div(
    class = "form-group shiny-input-container",
    style = if (!is.null(width))
      paste0("width: ", htmltools::validateCssUnit(width), ";"),
    if (!is.null(label))
      shiny:::controlLabel(inputId, label),
    do.call(
      tags$input,
      list(
        type = if (is.numeric(values) &
                   is.null(to)) {
          "number"
        } else {
          "text"
        },
        #class = "js-range-slider",
        id = inputId,
        name = inputId,
        value = ""
      )
    ),
    tags$style(
      whisker::whisker.render(
        template =
          "input[id='{{id}}'] {
        -moz-appearance:textfield;
}
input[id='{{id}}']::-webkit-outer-spin-button,
input[id='{{id}}']::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}", data = list(id = inputId))
    ),
    tags$script(
      HTML(
        whisker::whisker.render(
          template = '$("#{{id}}").ionRangeSlider({
          type: "{{data-type}}",
          from: {{data-from}},
          to: {{data-to}},
          grid: {{data-grid}},
          keyboard: true,
          keyboard_step: 1,
          postfix: {{data-postfix}},
          prefix: {{data-prefix}},
          drag_interval: {{data-drag-interval}},
          values: [{{data-values}}],
          disable: {{data-disable}}
          });',
          data = sliderProps
      )
      ))
      )
  if (identical(animate, TRUE)) 
    animate <- animationOptions()
  if (!is.null(animate) && !identical(animate, FALSE)) {
    if (is.null(animate$playButton)) 
      animate$playButton <- icon("play", lib = "glyphicon")
    if (is.null(animate$pauseButton)) 
      animate$pauseButton <- icon("pause", lib = "glyphicon")
    sliderTag <- htmltools::tagAppendChild(
      sliderTag,
      tags$div(class = "slider-animate-container", 
               tags$a(href = "#", class = "slider-animate-button", 
                      `data-target-id` = inputId, `data-interval` = animate$interval, 
                      `data-loop` = animate$loop, span(class = "play", 
                                                       animate$playButton), 
                      span(class = "pause", 
                           animate$pauseButton)))
    )
  }
  dep <- htmltools::htmlDependency(
    "ionrangeslider",
    "2.1.12",
    c(href = "shared/ionrangeslider"),
    script = "js/ion.rangeSlider.min.js",
    stylesheet = c(
      "css/ion.rangeSlider.css",
      "css/ion.rangeSlider.skinShiny.css"
    )
  )
  htmltools::attachDependencies(sliderTag, dep)
}

答案 1 :(得分:4)

Victorp解决方案很有效,值得称赞!我将发布与op集成的最终解决方案的代码。如果其他人想要运行此代码,请不要忘记包含Victorp的sliderValues函数。

library(shiny)
library(leaflet)
library(dplyr)

df <- data.frame(id = 1:5, 
             lat = c(45.53814, 45.51076, 45.4356, 45.54332, 45.52234), 
             lon = c(-73.63672, -73.61029, -73.6010, -73.56000, -73.59022),
             startDate = as.Date(c("2014-04-09", "2014-06-04", "2014-04-30", "2014-05-30", "2014-05-01")),
             year = c(2014, 2014, 2014, 2014, 2014),
             month = c(4, 6, 4, 5, 5),
             week = c(15, 23, 18, 22, 18),
             ym = as.Date(c("2014-04-01", "2014-06-01", "2014-04-01", "2014-05-01", "2014-05-01")),  # Year-Month
             yw = as.Date(c("2014-04-06", "2014-06-01", "2014-04-27", "2014-05-25", "2014-04-27"))   # Year-Week
)

# List of months
choices_month <- seq.Date(from = as.Date("2014-01-01"), by = "month", length.out = 36)

# ui
ui <- bootstrapPage(
  tags$style(type = "text/css", "html, body {width:100%;height:100%}"),

  leafletOutput("map", width = "75%", height = "100%"),

  absolutePanel(
top = 1,
right = 10,

div(
  style = "height: 180px;",
# custom slider function
sliderValues(
  inputId = "test", label = "Month", width = "100%",
  values = choices_month[4:6], 
  from = choices_month[4], to = choices_month[6],
  grid = FALSE, animate = animationOptions(interval = 1500)
), # end sliderInput
verbatimTextOutput("res")
    ) # end div
  ) # end absolutePanel
) # end bootstrapPage

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

  output$map <- renderLeaflet({
#    leaflet(data = df %>% filter(ym > as.Date(input$test[1]), ym < as.Date(input$test[2]))) %>% addTiles() %>% 
 leaflet(data = df %>% filter(ym == input$test[1])) %>% addTiles() %>% 
  addMarkers(~lon, ~lat) %>% 
  setView(lng = -73.6, lat = 45.52, zoom = 12)
  }) # end map

  output$res <- renderPrint({
    print(input$test) # you have to split manually the result by ";"
    print(as.Date(unlist(strsplit(input$test, ";"))))
    }) # end res
}) # end server

# App
shinyApp(ui = ui, server = server)