1

This is a follow-up question from This Question which was correctly answered by @K.Rohde. I am unable to call an input from UI as an argument in a user-defined function. Below is the complete reproducible shiny code:

library(shiny)
library(lubridate)
library(dplyr)

rm(list = ls())

Aging <- function(data, Transaction.Date, Report.Date = Sys.Date()){
  if(missing(Transaction.Date)) stop("You forgot to specify Transaction Date")
  Transaction.Date <- deparse(substitute(Transaction.Date))
  Report.Date <- deparse(substitute(Report.Date))
  data$year <- year(data[,Transaction.Date])
  data$Age <- car::recode(year(data[,Transaction.Date]), paste0("year(", Report.Date, ") = year(", Report.Date, "); year(", Report.Date, ") - 1 = year(", Report.Date, ") - 1; else = paste(year(", Report.Date, ")-2, 'And Prior')"))
  return(data)
}

ui <- fluidPage(

  dateInput("Report.Date", "Enter Cut-off Date"),
  actionButton("Submit", "Submit"),
  dataTableOutput("Aged.Data")

)

server <- function(input, output) {




    Debtors <- eventReactive(input$Submit, {
      data.frame(Names = c("John", "Mary", "Charles", "Peter", "David", "Fabian", "Aggrey", "Elizabeth", "Anthony", "Catherine"), Amount = seq(from = 100000, by = 600, length.out = 10), Transaction.Date = seq.Date(from = as.Date("2016/1/1"), by = "quarter", length.out = 10))
    })


    Aged.Data <- eventReactive(input$Submit, {
      Debtors() %>% Aging(., Transaction.Date, input$Report.Date)
    })



  output$Aged.Data <- renderDataTable(Aged.Data())

}

shinyApp(ui, server)

The error I get is as below:

in recode term:  else = paste(year(input$Report.Date)-2, 'And Prior')
  message: Error in year(input$Report.Date) : object 'input' not found

This seems to be from the part where I'm calling my Aging function and trying to reference the input$Report.Date as an argument, i.e,

Aged.Data <- eventReactive(input$Submit, {
      Debtors() %>% Aging(., Transaction.Date, input$Report.Date)

I've tried looking online for a similar solution through questions like This one and This one and though the error is similar, the proposed methods of resolving them seem to be unique to those questions.

I will appreciate any assistance on this.

Joseph.

6
  • Try this: Aged.Data <- eventReactive(input$Submit, { reportDate <- input$Report.Date Debtors() %>% Aging(., Transaction.Date, reportDate) Commented Apr 5, 2018 at 6:31
  • As an observation, when I use the Aging function without specifying the Report.Date parameter, the app works just fine, meaning that the Aging function is in order. Commented Apr 5, 2018 at 6:32
  • Thanks @Ika8, tried it but it throws the error that in recode term: else = paste(year(reportDate)-2, 'And Prior') message: Error in year(reportDate) : object 'reportDate' not found Commented Apr 5, 2018 at 6:37
  • @ika8, when I use 'Aged.Data <- eventReactive(input$Submit, { reportDate <<- input$Report.Date Debtors() %>% Aging(., Transaction.Date, reportDate) })' it works, but my worry with that is that any user can modify the Report.Date argument which will affect the output for other users. Commented Apr 5, 2018 at 6:57
  • You should NOT use <<- in that case.. Commented Apr 5, 2018 at 7:23

1 Answer 1

1

This is due to the combination of deparse and substitute.

Lets revisit what you are doing in simpler terms:

a <- function(val) {
  val <- deparse(substitute(val))
  val
}

b <- function() {
  c <- "someVariable"
  a(c)
}

With this setup you will get:

> b()
 [1] "c"

I hope you are aware of this and if not, make yourself. You basically are sending the variable name from the calling scope of b into the function scope of a. This is almost never what you want to happen in programming.

In your code sample, you are giving a variable name called input$Report.Date into the globally defined function Aging and you hope that it will be interpreted there in the right way. But of course, input is not an object in this function.

But why would it? Why do you have to interpret the variable in Aging. If you leave away the deparse(substitute(...)), you are not getting the variable name but the variable value. And nothing else is it that you want here.

I bet the recode function is to blame for you having to be aware of what is code to be interpreted and what are just values.

So the fix is to just leave away

Report.Date <- deparse(substitute(Report.Date))

Little Extra: If I were you, I would never use something like deparse and substitute unless there really is code involved that comes as plain text and must be interpreted.

Also recode seems to be so much over the line, since it just cloaks very simple modifications that you can easily do without this overcomplicated tool.

If I were you, my Aging function would look like this:

Aging <- function(data, dateColumnName, reportDate = Sys.Date()){
  if(missing(dateColumnName)) stop("You forgot to specify Transaction Date")

  maxDisplayYear <- year(reportDate) - 2
  data$Age <- year(data[,dateColumnName])
  data$Age[data$Age <= maxDisplayYear ] <- paste(maxDisplayYear, 'And Prior')

  return(data)
}

and you would call it in your example with

Debtors() %>% Aging("Transaction.Date", input$Report.Date)
Sign up to request clarification or add additional context in comments.

1 Comment

thanks for this detailed answer, it helped me rethink how I'm creating functions. And yes, recode was to blame for my attempt to separate what code should be interpreted and what code should return values. Your simplified Aging function did the trick. Again, many thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.