0

I am trying to create a shiny app that applies a self-made function to an uploaded dataset, then allows to download the modified results. Here is my code:

library(shiny)
library(tidyverse)

namkurz <- function(data, a_spalte) {
  kuerzel <- vector(length = length(data$a_spalte))
  for (i in 1:length(data$a_spalte)){
    spez = data$Art[i]
    s = unlist(strsplit(spez, " ", fixed = TRUE))
    s = substr(s, 1, 2)
    s = paste(s, collapse = ' ')
    kuerzel[[i]] = s
  }
  data <- data %>%
    mutate(kurz = kuerzel)
}


ui <- fluidPage(
  fileInput('upload','Deine Kartierungsdaten'),
  textInput('art', 'Wie heißt die Spalte mit Artnamen?'),
  downloadButton('analyse','Artenkürzel hinzufügen')
)
server <- function(input, output, session) {
  data <- reactive({
    req(input$upload)
    
    ext <- tools::file_ext(input$upload$name)
    switch(ext,
           csv = vroom::vroom(input$upload$datapath, delim = ";"),
           validate("Invalid file; Please upload a .csv file")
    )
  })
  art <- reactive(input$art)
  output$analyse <- downloadHandler(
    filename = function() {
      paste0('mit_kuerzel', ".csv")
    },
    content = function(file) {
      ergebnis <- reactive(namkurz(data(), art()))
      vroom::vroom_write(ergebnis(), file)
    }
  )
}
shinyApp(ui, server)

When trying to save the output I get a 'Warning: Unknown or uninitialised column:' error. I think my problem is in the assignment of argument 'art' to the 'ergebnis' object, but I can't find the way to fix it.

Tuck
  • 3
  • 2

1 Answers1

0

I recommend a few things:

  1. (Required) In your function, a_spalte is a character vector and not the literal name of column in the frame, so you need to use [[ instead of $, see The difference between bracket [ ] and double bracket [[ ]] for accessing the elements of a list or dataframe and Dynamically select data frame columns using $ and a character value.

    Change all references of data$a_spalte to data[[a_spalte]].

    namkurz <- function(data, a_spalte) {
      kuerzel <- vector(length = length(data[[a_spalte]]))
      for (i in 1:length(data[[a_spalte]])){
        spez = data$Art[i]
        s = unlist(strsplit(spez, " ", fixed = TRUE))
        s = substr(s, 1, 2)
        s = paste(s, collapse = ' ')
        kuerzel[[i]] = s
      }
      data <- data %>%
        mutate(kurz = kuerzel)
    }
    
  2. Your function is a bit inefficient doing things row-wise, we can vectorize that operation.

    namkurz <- function(data, a_spalte) {
      spez <- strsplit(data$Art, " ", fixed = TRUE)
      data$kurz <- sapply(spez, function(z) paste(substr(z, 1, 2), collapse = " "))
      data
    }
    
  3. (Optional) The content= portion of downloadHandler is already reactive, you do not need to wrap namkurz in reactive. Because of this, you also don't need to treat ergebnis as reactive.

      output$analyse <- downloadHandler(
        filename = ...,
        content = function(file) {
          ergebnis <- namkurz(data(), art())
          vroom::vroom_write(ergebnis, file)
        }
      )
    
  4. (Optional) Your output filename is fixed, so two things here: if it's always going to be "mit_kuerzel.csv", then there's no need for paste0, just use function() "mit_kuerzel.csv".

    However, if you are intending to return a file named something based on the original input filename, one could do something like:

        filename = function() {
      paste0(tools::file_path_sans_ext(basename(input$upload$name)),
             "_mit_kuerzel.",
             tools::file_ext(input$upload$name))
        },
    

    to add _mit_kuerzel to the base portion of the uploaded filename. Note that the file in the content= section is never this name, the new_mit_kuerzel.csv is the filename offered to the downloading browser as a suggestion, that is all.

  5. (Optional) You are using a .csv file extension in the downloadHandler, but the default for vroom::vroom_write is to use delim = "\t", which is not a CSV. I suggest either adding delim = ";" (or similar), or changing the returned filename extension to .tsv instead.

r2evans
  • 141,215
  • 6
  • 77
  • 149