knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "ws#>",
  fig.align = "center"
)

# directory to Lab
dirdl <- system.file("shiny",package = "Intro2R")

# create rmd link

library(Intro2R)

Introduction

The shiny server allows the data scientist to take an otherwise static application and make it dynamic. This is done through the use of a concept called "reactivity".

In this document we will begin from the basic structure of the server application and then introduce the detail of the parts which make the programming easy.

This is "magic" with some plans and rules.

A very good resource can be found here: https://mastering-shiny.org/index.html

Getting started

{width="200"}

The above book has a lot of detail which we can use and implement. In order to jump start the process of learning we will ask and answer a question.

Question

How can we make a shiny table for any data set? We will attempt to answer this question and then after viewing our solution ask more questions answer these and repeat. In so doing we will come to grasp the essentials of "reactive programming".

Getting started

The basic idea is to carry out the following:

Data Input

The way shiny does this is by using various widgets. The first widget we will use is the shiny function fileInput()

fileInput(
  inputId,
  label,
  multiple = FALSE,
  accept = NULL,
  width = NULL,
  buttonLabel = "Browse...",
  placeholder = "No file selected"
)

The function has 7 options, the first is:

The rest we will leave for now but you can read more from the documentation.

Notice that the widget does not actually read in the data, the widget will provide the name of the file - is this all the information required to read in the data? No! We need to know whether the file has a header or not.

To get this information we need another widget, checkboxInput().

checkboxGroupInput(
  inputId,
  label,
  choices = NULL,
  selected = NULL,
  inline = FALSE,
  width = NULL,
  choiceNames = NULL,
  choiceValues = NULL
)

As with the previous widget there is the input id:

The rest are easy and we will leave them for now. In our case we need to ask the user to select whether the data have a header or not. We will assign one choice "TRUE" and then leave selected to "NULL".

Make a ui

A possible user input for a shiny app would go like the following:

 ui <- fluidPage(
      sidebarLayout(
        sidebarPanel(
          fileInput("fileName", "Choose a CSV File", accept = ".csv"),
          checkboxInput("header", "Has a header!", TRUE)
        ),
        mainPanel(
          tableOutput("fileContents")
        )
      )
    )

Notice that a lot of the above code is simply setting up formatting for the html page that will hold the various parts:

The ui functions to place input and output on the html page and also to supply the server with information concerning the file (name, header = TRUE/FALSE)

The picture below shows you some of what the above UI does.

The server

Now that we have made the ui functional part of the app we shall go to the second functional part called server.

In all shiny apps there are two lists that are automatically made by the shiny package:

To get the value selected by the user when using the widgets the technique is to use the input$id, list and id name.

The server function uses two inputs which are the two lists named above. To populate the output area defined in the ui we simply fill the output list appropriately. We may have defined many different places for output in which case we would populate the output list using the appropriate ids. In our case we have one area of output and it is a table.

We therefore use output$fileContents <- renderTable() Notice that the function is "renderTable". All shiny server functions that populate the output will need to be "render" functions.

The server function is given below:

server <- function(input, output) {
      output$fileContents <- renderTable({
        newfile <- input$fileName
        ext <- tools::file_ext(newfile$datapath)

        req(newfile)
        validate(need(ext == "csv", "Please upload a csv file"))

        read.csv(newfile$datapath, header = input$header)
      })
    }

Note that the read.csv() is called last after req() and validate() functions are invoked.

Why?

The following shows how the app will run after a wrong file is selected:

The above picture shows the message but read.csv is not called on the wrong file due to validate().

If both the req() and validate() are removed this is what you would see when the app is run:

The header is determined by the input$header component of the input list.

Putting it all together

Notice that the app placed in the r chunk below cannot be run when the RMD is knitted so we use the condition on interactive() - when knit, the R chunk will give a FALSE interactive result. Hence the app will not be run. You can run it by selecting the app in the RMD document.

## Only run examples in interactive R sessions
interactive()
if (interactive()) {

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      fileInput("fileName", "Choose a CSV File", accept = ".csv"),
      checkboxInput("header", "Has a header", TRUE)
    ),
    mainPanel(
      tableOutput("fileContents")
    )
  )
)

server <- function(input, output) {
  output$fileContents <- renderTable({
    newfile <- input$fileName
    ext <- tools::file_ext(newfile$datapath)

    req(newfile)
    validate(need(ext == "csv", "Please upload a csv file"))


    read.csv(newfile$datapath, header = input$header)
  })
}

shinyApp(ui, server)
}

Run the app

The following is the output when data file is selected:

New Question

Can we make an app that will read in data and allow the user to make selection of variables to create a suitable plot?

To answer this question we can modify the above code. There seems to be a problem though. How can we make a widget to identify the file to read and then have a widget that selects variables when that can only happen if the file is read. That is the second widget must display variables which depends on the existence of a file in the workspace.

Modifying the UI

library(ggplot2)

ui <- fluidPage(

  titlePanel("Plotting data"),

  sidebarLayout(

    sidebarPanel(

      fileInput("file", "Select csv file:", accept = ".csv"),
      checkboxInput("header", "The file has a header!", TRUE),
      br(),

      varSelectInput("var1", "Variable 1:", data()),
      varSelectInput("var2", "Variable 2:", data()),

      checkboxInput("scatter", "Scatter plot", TRUE)


         ),

    mainPanel(

      plotOutput("plot1")




    )
  )
)

server <- function(input, output, session) {
  # read input file and update choices for variable selection widget
  data <- reactive({
    req(input$file)
   read.csv(input$file$datapath, header = input$header)

  })



  # create first plot given data and selected variables
  output$plot1 <- renderPlot({
    req(input$file, input$var1, input$var2)

    # store selected variables in df
    df <- data()[,c(input$var1, input$var2)]

    # create ggplot
    g <- ggplot(df, aes(x = df[,1], y = df[,2])) +
      labs(title = paste("Plot of", input$var1, "vs.", input$var2), x = input$var1, y = input$var2) 

    # add regression line and/or marginal plots given user input
    if (input$scatter) {
      g <- g + geom_smooth(method = "lm")

    }
    else {
      g <- g + geom_boxplot()
    }
    print(g)
  })


}

# Run the application
shinyApp(ui = ui, server = server)


MATHSTATSOU/Intro2R documentation built on Feb. 20, 2025, 6:18 a.m.