0

I'm making a Shiny app in which the user can generate a column in a table by clicking on a checkboxInput. The column I would like to create contains the lagged value of the column already present in the table.

The code below shows a reproducible example: there are two individuals (A and B) and three time periods (1, 2 and 3).

library(dplyr)
library(shiny)

data <- head(mtcars)
data$time <- rep(seq(1:3))
data$ID <- rep(c("A", "B"), each = 3)

ui <- fluidPage(
  selectInput("choice", "Select a column", choices = c("mpg", "drat", "hp"), multiple = F),
  checkboxInput("lag", "Compute lag value"),
  tableOutput("table")
)

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

  data2 <- reactive({
    lagged_name <- paste0(input$choice, "_lagged")
    if (input$lag){
      data %>%
        select(ID, time, input$choice) %>%
        group_by(ID) %>%
        mutate(!!all_of(lagged_name) := lag(data[, input$choice]))
    }
    else {
      data %>%
        select(ID, time, input$choice)
    }
  })

  output$table <- renderTable({
    data2()
  })
}

shinyApp(ui, server)

When I run this code and click on the checkbox, I have the error:

Warning: Error in : Column mpg_lagged must be length 3 (the group size) or one, not 6

Thanks to this answer, I corrected it by adding order_by = ID in the lag function but now there is another problem: for individual 1, it creates the right lagged values, but then those values are repeated for individual 2 as well whereas they do not correspond.

I tried a similar example without the Shiny environment and the right output is produced so I suppose this problem comes from the inputs or reactive environment.

Does anybody have a solution?

bretauv
  • 7,756
  • 2
  • 20
  • 57

1 Answers1

1

There are some (minor) issues with non-standard evaluation (NSE) inside your reactive data object. Fixing these gives

library(dplyr)
library(shiny)

data <- head(mtcars)
data$time <- rep(seq(1:3))
data$ID <- rep(c("A", "B"), each = 3)

ui <- fluidPage(
  selectInput("choice", "Select a column", choices = c("mpg", "drat", "hp"), multiple = F),
  checkboxInput("lag", "Compute lag value"),
  tableOutput("table")
)

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

  data2 <- reactive({
    lagged_name <- paste0(input$choice, "_lagged")
    if (input$lag){
      data %>%
        select(ID, time, input$choice) %>%
        group_by(ID) %>%
        mutate(!!lagged_name := lag(!!sym(input$choice)))
    }
    else {
      data %>%
        select(ID, time, input$choice)
    }
  })

  output$table <- renderTable({
    data2()
  })
}

shinyApp(ui, server)

resulting in

enter image description here

Explanation:

  • select takes both evaluated symbols and strings as arguments, so we can directly pass input$choice as an argument to select.
  • To construct a new column with a name from a variable we need to evaluate the variable as !!lagged_name; we then must use := (instead of =) to do the assignment, as R's grammar does not allow expressions as argument names (the lhs of the assignment). Finally, inside the lag function we first must convert input$choice to a symbol with sym and then evaluate the symbol with !!. That's because of dplyr's NSE, where you would write e.g. mtcars %>% mutate(col = lag(wt)) and not mtcars %>% mutate(col = lag("wt")).
Maurits Evers
  • 49,617
  • 4
  • 47
  • 68
  • Thanks for your answer, it works well. I'm not very comfortable with NSE (not even sure of what it means) because I used it from another post on SO, could you explain me a little bit more or redirect me on an article dedicated to it? – bretauv Feb 13 '20 at 08:30
  • Hi @bretauv; I've added some explanations, hopefully it's useful. The best reference for some more reading is IMO Hadley's [Advanced R](https://adv-r.hadley.nz/), specifically the section on quasiquotation. – Maurits Evers Feb 13 '20 at 21:40
  • 1
    Thanks for the explanation, it is useful. If I understand well, ```sym``` is necessary to remove the quotation marks that come automatically with ```input```, right? I will read hadley's book, thanks also for the reference – bretauv Feb 13 '20 at 21:54