2

I'm teaching myself Shiny and I am stuck on my ggplot2 graph not being able to use the reactive dateRangeInput as my x-axis. I have a few questions:

  1. Is there a way to use my data frame to grab the min, max values for date range input instead of having to hardcode them in so that when I add more tweets to the data frame I don't have to hardcode the values each time?
  2. I am getting the error: Aesthetics must be either length 1 or the same as the data (33108): x, y when I try to use input$date as my aes(x = input$date...

library(shiny)
library(tidyr)
library(ggplot2)

tweets <- read.csv(file.choose())
colnames(tweets)[1] <- "Content"
tweets <- separate(tweets, created_at, c("Date", "Time"), sep = " ")
tweets$Date <-as.Date(tweets$Date, "%m/%d/%Y")

ui <- fluidPage(
  dateRangeInput(inputId = "date", 
              strong("Date Range"),
              start = "2009-05-04", end = "2018-02-28",
              min = "2009-05-04", max ="2018-02-28" ),
  plotOutput("Graph")
)

server <- function(input, output) {
  output$Graph <- renderPlot({
    ggplot(tweets, aes(x = input$date, y = count)) + 
      geom_bar(stat = "identity", position = "stack") +
      #scale_y_continuous(name = "Retweet Count", limits = c(0,370000), breaks=seq(0,370000,10000)) +
      theme(panel.background = element_rect(fill = "white", colour = "grey50")) 
  })
}

shinyApp(ui = ui, server = server)

2 Answers2

5

@Pete900's answer summarizes the use of updateDateRangeInput well, for further information you can refer to this part of the shiny documentation.

About your second problem: input$date will return a vector of length 2 with the first element beeing the lower and the second being the upper part of the selected range. You will most likely not use this directly as x-aesthetics but rather subset your data with this and then plot the newly subsettet data. You can e.g. write

library(dpylr) # alternatevly library(tidyverse)
newtweets <- reactive({
filter(tweets, between(date ,input$date[1], input$date[2]))
})

then, in your ggplot, use newtweets() as your data.

Update The functions filter and between() (which is a shortcut for x is greater than ... and lesser then ...) come fromt the package dplyr, which is great for working with dataframes and part of a collection of packages that play very nicely with each other called tidyverse (see here).

When you refer to the newly created reactive object newtweets(), make sure to not forget the paranthesis because it is now a function call, that enables shiny to update the dataframe should the input change.

Update

A full working example in which I create some artificial data:

library(shiny)
library(tidyverse)
library(lubridate)

# tweets <- read.csv(file.choose())

st <- ymd("2009-05-01")
en <- ymd("2018-02-28")
dates <- seq.Date(from = st, to = en, by = 1)
tweets <- tibble(date = dates, count = rnorm(length(dates), mean = 5, sd = 3))


ui <- fluidPage(
    dateRangeInput(inputId = "date",
                   strong("Date Range"),
                   start = "2009-05-04", end = "2018-02-28",
                   min = "2009-05-04", max ="2018-02-28" ),
    plotOutput("Graph")
)

server <- function(input, output) {

    newtweets <- reactive({
        filter(tweets, between(date ,input$date[1], input$date[2]))
    })

    output$Graph <- renderPlot({
        ggplot(newtweets(), aes(x = date, y = count)) +
            geom_bar(stat = "identity", position = "stack") +
            #scale_y_continuous(name = "Retweet Count", limits = c(0,370000), breaks=seq(0,370000,10000)) +
            theme(panel.background = element_rect(fill = "white", colour = "grey50"))
    })
}

shinyApp(ui = ui, server = server)
Jannik Buhr
  • 1,788
  • 2
  • 11
  • 11
  • Ok so filter will find the rows where the min, max values are true and between will grab everything in between. Will it grab all the other columns in the df? And so if I'm understanding this correctly I can use newtweets as the df to plot in the ggplot and that will be reactive now? – Matthew Kirshy Mar 21 '18 at 21:03
  • I am sorry, I did not include the packages I use and will update my answer. – Jannik Buhr Mar 21 '18 at 22:37
  • I get the error when I add your code in `Warning: Error in filter_impl: Evaluation error: object 'input' not found.` – Matthew Kirshy Mar 21 '18 at 22:52
  • so is newtweets() the new x axis or the new df to plot in ggplot? – Matthew Kirshy Mar 21 '18 at 22:53
  • It will be your new data / dataframe to put in the plot. The reactive function I made must be inside the server function, maybe the error comes from putting it elsewhere. See https://shiny.rstudio.com/articles/reactivity-overview.html for more information. – Jannik Buhr Mar 21 '18 at 23:22
  • thank you. Unfortunately now I get this error: `Error in filter_impl: Evaluation error: Not compatible with requested type: [type=closure; target=double]..` I appreciate the help so far but if you have any suggestions on how to fix this error I would be grateful. – Matthew Kirshy Mar 21 '18 at 23:28
  • can you provide a repex ( a minimal reproducible example, to make it easy for people to reproduce your error and then fix it)? As we don't have your csv file it is considered good practive to incluce minimal example data in the form of code. The r package reprex can help you witht this: https://github.com/tidyverse/reprex – Jannik Buhr Mar 22 '18 at 10:05
  • See the update for a working examples of how it should look in your code. – Jannik Buhr Mar 22 '18 at 11:49
  • What if we made the limits of `scale_x_date` , the `input$date[1]` & `input$date[2]` ? Then the plot would re-render when these dates changed. Or is it better practice to make the data set reactive? – kraggle Aug 08 '22 at 16:19
  • This would also work. Sometimes you want to have other things that depend on the same data, like e.g. a download button for the subset of data shown on the plot. If this is a requirement or you think it might be a requirement in the future, it is the better option to make the data itself reactive. – Jannik Buhr Jan 03 '23 at 10:16
2

For the first question you can use updateDateRangeInput see here. So you would find your min and max dates in tweets outside of the server function then pass them to the input. Make sure to add session to your function:

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

   observe({
    updateDateRangeInput(session, "date", min = myMinDate, max = myMaxDate)
   })
}

For the second question you need to use aes_string to pass variables to ggplot, see here or here.

Pete900
  • 2,016
  • 1
  • 21
  • 44
  • Ok so will the `aes_string` pull the date data from the tweets df and plot based on that or will it take the values from my min, max values, fill in the rest and plot based on matching up those dates to the date column in the tweets df? Also will I be able to plot the y axis from the tweets df or another new df? – Matthew Kirshy Mar 20 '18 at 17:08
  • See @Jannik Buhr's answer for details on that. – Pete900 Mar 20 '18 at 17:17