0
votes

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.

1
Try this: Aged.Data <- eventReactive(input$Submit, { reportDate <- input$Report.Date Debtors() %>% Aging(., Transaction.Date, reportDate) - Ika8
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. - Joseph Kigotho
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 - Joseph Kigotho
@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. - Joseph Kigotho
You should NOT use <<- in that case.. - Ika8

1 Answers

1
votes

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)