Search code examples
rdateshinycoerce

How can I make data uploaded to a shiny app usable and then actually use it?


I'm trying to build a shiny app and I've never done this before.

What I want to do is allow the user to upload data from a CSV file of the user's choice that looks something like this:

ID    Date        Name        Amount
123   01/12/2018  John Doe    100
124   01/02/2018  Bob Smith   110
125   01/03/2018  Jane Jones  100
126   01/02/2018  John Doe    110
127   01/03/2018  Bob Smith   100
128   01/04/2018  Jane Jones  110
129   01/03/2018  John Doe    100
130   01/04/2018  Bob Smith   110
131   01/04/2018  Jane Jones  120
132   01/14/2018  Tom Thomas  100

Then I want to have the app USE this data to run code such as this:

as.Date(myDF$Date, format="%m/%d/%Y")
sortedByDate = myDF[order(myDF$Date)]
amountPerDate = aggregate(data=myDF, Amount ~ Date, sum)
amountPerDate

to produce output such as this:

Date        Amount
01/02/2018  220
01/03/2018  300
01/04/2018  340
01/12/2018  100
01/14/2018  100

so the user can see the total amount per date, with the dates in correct order. I've been doing stuff like this in regular R script for months but have never before tried it in shiny.

So to upload the data into shiny I'm using this code:

shinyServer(function(input, output){
 output$contents <- renderTable({
  inFile <- input$file1
  if(is.null(inFile))
   return(NULL)
  userData = read.csv(inFile$datapath, sep=",")

  #here I run chunks of code to clean the data

  #assign data frame to global environment
  assign("userData", userData, envir = .GlobalEnv)

  #display data table in app
  userData
})

So now the user can look at the data but can't DO anything with it.

The data frame in the global environment has every variable as class Factor. I thought it would be easy to coerce the variables into their proper classes (Numeric for "ID" and "Amount", Character for "Name" and Date for "Date" but I can't do it. The very next thing I try to make the app do is display the date in the first row. I tried it with this code:

output$text <- renderText({
 userData$Date  = as.Date(as.character(userData$Date), format = "%m/%d/%Y")
 userData[1,2]
})

But the output in the app is 17533, NOT 01/12/2018 like I expected.

For some weird reason the following code WILL produce an output of 01/12/2018:

output$text <- renderText({
 userData$Date = as.character(userData$Date)
 userData[1,2]
})

but I need the dates to be class DATE, not class character.

Without the ability to coerce the variables into their proper classes my entire app is useless. Please help.


Solution

  • Don't use assign in Shiny, that is bad practice and it also doesn't allow for any reactivity. Instead, use a reactive to store your input data.

    Regarding the unexpected printing output, this probably has to do with the way dates are stored. If you output a Date object in renderText, it will apparently parse it to it's integer value and convert that to character. To prevent that, parse it to character yourself first.

    Here is a working example:

    library(shiny)
    library(data.table)
    server <- function(input, output, session) {
    
      filedata <- reactive({
        infile <- input$file1
        if (is.null(infile)) {
          return(NULL)
        }
        df <- fread(infile$datapath)
        return(df)
      })
    
      output$my_table <- renderDataTable({
        if(is.null(filedata()))
        {
          return(NULL)
        }
        else
        {
          myDF <- filedata() # read the uploaded data from reactive
          myDF$Date <- as.Date(myDF$Date, format="%m/%d/%Y") # Change format!
          sortedByDate = myDF[order(myDF$Date)]
          amountPerDate = aggregate(data=myDF, Amount ~ Date, sum)
          print('done')
          return(amountPerDate)
        }
      })
    
      output$my_text <- renderText({
        if(is.null(filedata()))
        {
          return(NULL)
        }
        else
        {
          as.character(as.Date(filedata()$Date, format="%m/%d/%Y"))[1]
        }
      })
    }
    
    
    ui <- fluidPage(
      fileInput("file1", "Choose CSV File",
                accept = c(
                  "text/csv",
                  "text/comma-separated-values,text/plain",
                  ".csv")
      ),
      dataTableOutput('my_table'),
      textOutput('my_text')
    )
    
    shinyApp(ui,server)
    

    Hope this helps!