1

I would like to be able to upload a dataset, select a set of columns, transform the selected columns (i.e. apply a function), then download the modified file. I have been trying to do so with the following code:

library(shiny)
library(DT)
library(shinyWidgets)
library(plyr)
library(dplyr)
library(RecordLinkage)
library(readxl)

cleanup <- function(x){
  x <- as.character(x) # convert to character
  x <- tolower(x) # make all lowercase
  x <- trimws(x, "both") # trim white space
  return(x)
}

ui <- fluidPage(
  h2("Record Linkage Data"),
  fileInput("file1", "Upload file for cleaning", accept = c("xls", "csv"), multiple = F),
  actionButton(inputId = "clean", label = "Clean Data"),
  downloadButton("download1", "Download file1"),
  pickerInput(width = "75%",
              inputId = "pick_col1",
              label = "Select columns to display",
              choices = colnames(file1),
              selected = colnames(file1),
              options = list(
                `actions-box` = T,
                `selected-text-format` = paste("count > ", length(colnames(file1)) - 1),
                `count-selected-text` = "Alle",
                liveSearch = T,
                liveSearchPlaceholder = T
              ),

              multiple = T),
  DT::dataTableOutput("mytable")
)

load_path <- function(path) {
    req(input$file)
    ext <- tools::file_ext(path)

    if (ext == "csv"){
      read.csv(path, header = T)
    } else if (ext == "xls" || ext == "xlsx"){
      read_excel(path)
    } else{
      stop("Unknown extension: '.", ext, "'")
    }
}

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

file1 <- reactive(load_path(input$selection$datapath[[1]]))

#file2 <- reactive(load_path(input$selection$datapath[[2]]))
  eventReactive(input$clean, {
    output$mytable <- DT::renderDataTable({
      data.frame(lapply(select(file1, input$pick_col1), cleanup))
    })


  })

  output$download <- downloadhandler(
    filename = function(){
      paste0(tools::file_path_sans_ext(input$filename), ".csv")

    },

    content = function(file){
      write.csv(data(), file)
    }
  )

}  

shinyApp(ui, server)

When I run the above code, I get the error : Error in is.data.frame(x) : object 'file1' not found. I am unsure why this is but I have been struggling to understand naming things in shiny. For example: I want to upload file1, then transform it. Do I continue to refer to file1 when I want to download it? These may seem like silly questions but I am asking because I don't know and I'm trying to learn. There seem to be lots of different approaches.

I would like to: 1. Load a file 2. Select columns (pickerInput is what I have been trying, but selectInput would suffice I suppose) 3. via action button, apply a pre-specified function to the selected columns 4. download the transformed dataset as a .csv

halfer
  • 19,824
  • 17
  • 99
  • 186
jvalenti
  • 604
  • 1
  • 9
  • 31
  • 2
    In the line in your `eventReactive(...)` don't you need to call `select(file1(), input$pick_col1)`, not `select(file1, input$pick_col1)`? Make sure you're calling your reactives with parentheses. Then see if the error persists. – Brian Feb 08 '20 at 23:19
  • I tried this, the error persists. – jvalenti Feb 23 '20 at 22:01

1 Answers1

4

I've encountered some problems

  1. It is a very silly one (it happens to all of us). You should write downloadHandler instead of downloadhandler.
  2. The main problem: Your pickerInput is trying to select the column names of the data frame file1 when it does not exists. When you run the application the code is trying to find a file1 data frame and look its column names, but since at that time you haven't uploaded anything yet, it throws an error.
  3. On how you read files: I am not familiar with how you read files, I suggest you do something similar than what is done in this example. https://shiny.rstudio.com/gallery/file-upload.html. Note you need to use a read.* function and point the result to another name, df in the example.

How would I solve it: 1. Set choices and selected options to NULL by default. Something like the following should work:

  pickerInput(width = "75%",
              inputId = "pick_col1",
              label = "Select columns to display",
              choices = NULL,
              selected = NULL,
              options = list(
                `actions-box` = T,
                # `selected-text-format` = paste("count > ", length(colnames(file1)) - 1),
                `count-selected-text` = "Alle",
                liveSearch = T,
                liveSearchPlaceholder = T
              ),
              multiple = T)
  1. Add an updatePickerInput in the server side within an observeEvent. Something like this should work.
observeEvent(input$file1, {

  req(input$file1) # ensure the value is available before proceeding

  df <- read.csv(input$file1$datapath)

  updatePickerInput(session = session, 
                    inputId = "pick_col1", 
                    choices = colnames(df), 
                    # ... other options) 
})

I haven't looked much if there are other problems with the code. I suggest you start from the example in the link shared and start modifying it until you get what you want.

If that does not work, let me know and I can try to figure it out

Good luck!

Tomas Capretto
  • 721
  • 5
  • 6