14

I am writing a shiny and and wanted a slider for the date. The date in my data are monthly and I would like to step forward one month at a time. The docs for the slider input say that the step value is either in second or days depending on the min/max parameter types. Currently I have:

sliderInput("slider", "Time", min=as.Date("2005-01-01"),
                              max=as.Date("2014-12-01"),
                              value=as.Date("2005-01-01"), step = 30,...)

I want to be able to step by month instead of by day but it doesn't seem possible from what they give me. Is there a snippet of js I could add that would give me this functionality?

Clarification Note: I have read the docs for this function and to my best understanding there is no base functionality for this. The time format parameter, upon testing, only changes the labels not the values. I have seen a couple posts that access the values of certain widgets and was wondering if this was possible. Eg)

<script type="text/javascript">
    $(document).ready(function() {
    var slider = $("#slider").slider();
 // override the default "nice" function.
    slider.nice = function(value) {
    var ref_date = new Date("2005-01-01");
 // each slider step is 1 day, translating to 24 * 3600 * 1000 milliseconds
    var slider_date = new Date(ref_date.getTime() + value * 24 * 3600 * 1000);
                          return [slider_date.getUTCFullYear(), 
                          slider_date.getUTCMonth() + 1, 
                          slider_date.getUTCDate()].join("-");
                          }
                          })
Marsenau
  • 925
  • 11
  • 18

2 Answers2

17

There is a timeFormat function within the sliderInput. For more information visit Slider Input Widget.

EDIT:

To get the dates out and use them later on in your analysis, much credit goes to this question First day of the month from a POSIXct date time using lubridate and the function provided by Roland.

rm(list=ls())
library(shiny)
monthStart <- function(x) {
  x <- as.POSIXlt(x)
  x$mday <- 1
  as.Date(x)
}
ui <- basicPage(sliderInput("slider", "Time", min = as.Date("2010-01-01"),max =as.Date("2014-12-01"),value=as.Date("2014-12-01"),timeFormat="%b %Y"),
                textOutput("SliderText")
                )
server <- shinyServer(function(input, output, session){

  sliderMonth <- reactiveValues()
  observe({
    full.date <- as.POSIXct(input$slider, tz="GMT")
    sliderMonth$Month <- as.character(monthStart(full.date))
  })
  output$SliderText <- renderText({sliderMonth$Month})
})
shinyApp(ui = ui, server = server)

enter image description here

Community
  • 1
  • 1
Pork Chop
  • 23,306
  • 4
  • 50
  • 63
  • 4
    This doesnt give me monthly stepping functionality. It only changes the labels on the slider to be equivalent to the format parameter. I tested it and the date still advances by step if provided and by day if not. – Marsenau Jun 08 '16 at 12:41
  • @Marsenau -- I had exactly the same need as you, for a slider that actually advances one month at a time and, to my delight, found a solution in [this terrific answer](http://stackoverflow.com/a/40402610/980833). – Josh O'Brien May 08 '17 at 20:03
  • @JoshO'Brien, I think the entire slider was re-written in that horrific answer, which I think is unnecessary. I think it would be easier to ask `shiny` devs to add this functionality in one of their updates – Pork Chop May 09 '17 at 06:39
  • @PorkChop It seems like the [Ion.RangeSlider](https://github.com/IonDen/ion.rangeSlider)'s `values=` argument, which allows one to specify character vectors as inputs, it the key bit that I need. As far as you know, do any of **shiny**'s current input sliders support that option, and if not, is that the feature you'd suggest I ask the **shiny** devs to implement? – Josh O'Brien May 09 '17 at 16:04
  • 1
    @PorkChop Looks like there are already a number of related requests, both [for that specific feature](https://github.com/rstudio/shiny/issues/1095), and for [more general access](https://github.com/rstudio/shiny/issues/1383) to all of the options Ion.RangeSlider makes possible. (Found those both via a link from [this SO answer](http://stackoverflow.com/questions/31299254/what-advantages-does-the-new-ion-rangeslider-bring-to-shiny)). – Josh O'Brien May 09 '17 at 16:13
4

This is a solution that I use, but first I'd like to explain the logic:

  1. As mentioned above, timeFormat argument of the sliderInput() function does not give the required 1-month-step functionality. Instead, it simply formats underlying daily data, so stepping remains intact at a daily increment.
  2. sliderInput() has another argument step which can be used to explicitly define the stepping increment. However, it requires a single integer as an input, and therefore doesn't accept vectors/ranges. Given that months are of different lengths, a single integer is not right to use here. I tried to use lubridate::months(1) and lubridate::period(1, units = "months") -- to no avail unfortunately as both were automatically converted to an integer of 30 once rendered by the app, hence monthly increments were not retained.

I found a solution with shinyWidgets::sliderTextInput() which creates a character slider.

Assuming your dates are stored as yyyy-mm-dd dates in column Date of table d:

sliderTextInput(
  inputId    = "myID",
  label      = "myLabel",
  choices    = as.yearmon(unique(d$Date)),
  selected   = c(as.yearmon(min(d$Date)), as.yearmon(max(d$Date))),
  grid       = TRUE,
  width      = "100%"
)

With this you always step at monthly increments. as.yearmon() is used so your app shows slider labels in the MMM YYYY format.

Bear in mind that slider output is a character, so you need to back-transform into date:

  • as.Date(as.yearmon(input$myID[1])) for a start
  • as.Date(as.yearmon(input$myID[2])) for an end