2

I have merged different sources of code to make an app that allows one to upload a file (data frame).

However, beyond this I would also like to make it possible to select specific columns from the data frame and analyse them. This is difficult however as one must predefine the given data frame in order to be able to refer to it in the ui.R script.... So when a previously undefined data frame is uploaded to the site, one can not revere to it in the ui.R as it is defined in the server....

predefined variables

vchoices <- 1:ncol(mtcars)
names(vchoices) <- names(mtcars)

ui.R

    runApp(
      ui = basicPage(
        h2('The uploaded file data'),
        dataTableOutput('mytable'),
        fileInput('file', 'Choose info-file to upload',
                  accept = c(
                    'text/csv',
                    'text/comma-separated-values',
                    'text/tab-separated-values',
                    'text/plain',
                    '.csv',
                    '.tsv'
                  )
        ),
        actionButton("choice", "incorporate external information"),

        selectInput("columns", "Select Columns", choices=vchoices, inline = T),
        #notice that the 'choices' in selectInput are set to the predefined 
        #variables above whereas I would like to set them equal to the 
        #not yet defined uploaded file below in server.R

        tableOutput("table_display")
      ))

Notice that the 'choices' in selectInput are set to the predefined variables above whereas I would like to set them equal to the not yet defined uploaded file below in server.R

server.R

  server = function(input, output) {

      info <- eventReactive(input$choice, {
        inFile <- input$file
        if (is.null(inFile))
          return(NULL)
        isolate(f<-read.table(inFile$datapath, header = T,
                               sep = "\t"))
        f
      })
      output$table_display<-renderTable({
        f<-info()
        f<-subset(f, select=input$columns) #subsetting takes place here
        head(f)
      })
    }

Does anyone know of a way to refer to a variable that's defined in in the server, in the ui and thus allow for interactive manipulation?

johnny utah
  • 269
  • 3
  • 17

1 Answers1

6

You can use a family of functions update*Input - in this case updateSelectInput. Its first argument has to be session and you also have to add session to server <- function(input, output) to be able to update your widget.

You could make an update of the widget immediately after clicking on the actionButton - so, you had to use updateSelectInput within eventReactive.


Let's take a look how we can do that:

First, you can save the names of columns of the new uploaded dataset in a variable, say, vars and then pass it to the function updateSelectInput. (The choices of the selectInput are initially set to NULL - we don't need to specify them before because they are going to be updated anyway)

info <- eventReactive(input$choice, {
    inFile <- input$file
    # Instead # if (is.null(inFile)) ... use "req"
    req(inFile)

    # Changes in read.table 
    f <- read.table(inFile$datapath, header = input$header, sep = input$sep, quote = input$quote)
    vars <- names(f)
    # Update select input immediately after clicking on the action button. 
    updateSelectInput(session, "columns","Select Columns", choices = vars)

    f
  })

I've added a small upload interface to your code.

The other way would be to define widgets on the server side and then to pass them to the client side via renderUI function. You can find here an example.


Full example:

library(shiny)

ui <- fluidPage(
  h2('The uploaded file data'),
  dataTableOutput('mytable'),
  fileInput('file', 'Choose info-file to upload',
            accept = c(
              'text/csv',
              'text/comma-separated-values',
              'text/tab-separated-values',
              'text/plain',
              '.csv',
              '.tsv'
            )
  ),
  # Taken from: http://shiny.rstudio.com/gallery/file-upload.html
  tags$hr(),
  checkboxInput('header', 'Header', TRUE),
  radioButtons('sep', 'Separator',
               c(Comma=',',
                 Semicolon=';',
                 Tab='\t'),
               ','),
  radioButtons('quote', 'Quote',
               c(None='',
                 'Double Quote'='"',
                 'Single Quote'="'"),
               '"'),
  ################################################################

  actionButton("choice", "incorporate external information"),

  selectInput("columns", "Select Columns", choices = NULL), # no choices before uploading 

  tableOutput("table_display")
)

server <- function(input, output, session) { # added session for updateSelectInput

  info <- eventReactive(input$choice, {
    inFile <- input$file
    # Instead # if (is.null(inFile)) ... use "req"
    req(inFile)

    # Changes in read.table 
    f <- read.table(inFile$datapath, header = input$header, sep = input$sep, quote = input$quote)
    vars <- names(f)
    # Update select input immediately after clicking on the action button. 
    updateSelectInput(session, "columns","Select Columns", choices = vars)

    f
  })

  output$table_display <- renderTable({
    f <- info()
    f <- subset(f, select = input$columns) #subsetting takes place here
    head(f)
  })
}
shinyApp(ui, server)
Michal Majka
  • 5,332
  • 2
  • 29
  • 40
  • works perfectly except for the fact that I get an error when I initially input the data as no columns have been selected- which looks a bit messy.... is there a way to postpone the table display until such time as I have selected the columns e.g. if(input$columns){f <- subset(f, select = input$columns)}??? thanks so much – johnny utah Aug 09 '16 at 17:03
  • Of course, there is a very nice way of doing it with the function `req` or with`validate`. Just place in `render*` function as a frist line`req(input$xyz)`. You can read it as "require that input$xyz is available". Over [here](http://shiny.rstudio.com/articles/req.html) you can find a nice discussion which deals with handling missing inputs. – Michal Majka Aug 09 '16 at 17:11