Introduction to Biostatistical Computing PHC 6937

R Shiny

Zhiguang Huo (Caleb)

Monday September 26, 2022

R Shiny

Major references:

Credits to these resources:

The Training Materials are licensed under the Creative Commons Attribution-Noncommercial 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/us/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

Outlines

  1. R Shiny gallary
  2. Toy examples
  3. Deploy R Shiny app
  4. Server
  5. UI

R default example

We will see two key components:

R Shiny architecture

A minimum R Shiny example

library(shiny)
ui <- fluidPage()
server <- function(input, output) {}
shinyApp(ui = ui, server = server)
ui <- fluidPage("Hello World!")

UI, input and output

ui <- fluidPage(
# *Input() functions,
# *Output() functions
)

slider Input

# library(shiny)
ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100)
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

More on input

Try each one of these components

# library(shiny)
ui <- fluidPage(
  sliderInput(inputId = "num",label = "Choose a number",value = 25, min = 1, max = 100),
  actionButton(inputId = "actionButton",label="Action"),
  submitButton(text = "Submit"),
  checkboxInput(inputId = "checkbox", label = "Choice A"),
  checkboxGroupInput(inputId = "checkboxgroup", label = "Checkbox group", choices = c("A"="Choice 1", "B"="Choice 2")),
  dateInput(inputId = "dateInput", label = "date input", value = "2021-09-20"),
  fileInput(inputId = "fileInput", label = "File input"),
  numericInput(inputId = "numericInput", label = "Numeric input", value = "10"),
  textInput(inputId = "textInput", label = "Text input", value = "Enter text..."),
  radioButtons(inputId = "radioButton", label = "Radio buttons", choices = c("A"="Choice 1", "B"="Choice 2")),
  selectInput(inputId = "selectBox", label = "Select box", choices = c("A"="Choice 1", "B"="Choice 2"))
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Also see: https://shiny.rstudio.com/gallery/widget-gallery.html

Output

To display output, add an Output() function to the fluidPage(), separated by “,”

# library(shiny)
ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100),
  plotOutput(outputId="hist")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

In this example,

More on output

A simple server response

ui <- fluidPage(
  sliderInput(inputId = "num",label = "Choose a number",
              value = 25, min = 1, max = 100),
  plotOutput(outputId="hist")
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(100))
  })}
shinyApp(ui = ui, server = server)

3 rules for the server function

Example 0

ui <- fluidPage(
  sliderInput(inputId = "num",label = "Choose a number",
              value = 25, min = 1, max = 100),
  plotOutput(outputId="hist")
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num))
  })
}
shinyApp(ui = ui, server = server)

Input values

input argument in server matches inputID in ui

Output will automatically update if you follow the 3 rules

More on render

Run Shiny apps

Every Shiny app is maintained by a computer running R

Run Shiny apps on local laptop

setwd("/Users/zhuo/Desktop") ## change to your path
runApp("test", display.mode = "showcase")

runApp

save the code as ui.R and server.R

setwd("/Users/zhuo/Desktop") ## change to your path
runApp("test", display.mode = "showcase")

mode:

Run Shiny apps on server

There are many servers. - Shinyapps.io is a server maintained by R studio. - Free for basic usage.

Deploy on Shinyapps.io

Reference: https://shiny.rstudio.com/articles/shinyapps.html

  1. Go to https://www.shinyapps.io/

  2. Sign in and setup your account

  1. Follow the instruction
install.packages('rsconnect')
rsconnect::setAccountInfo
rsconnect::deployApp('path/to/your/app') ## setup account if you have multiple accounts

Server response: Reactivity

server <- function(input, output) {
  output$hist <- renderPlot({ hist(rnorm(100, input$num)) })
}
server <- function(input, output) {
  output$hist <- hist(rnorm(input$num)) ## this won't work
}

Reactivity in R is a two-step proces

  1. Reactive values notify the Reactive functions when they become invalid. This happens when you update your input values.

  2. The Reactive functions refresh. This will update the output result.

Reactive functions:

Example 1, two inputs

ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100),
  textInput(inputId = "title",
            label = "Write a title",
            value = "Histogram of Random Normal Values"),
  plotOutput("hist")
)
server <- function(input, output) {
  output$hist <- renderPlot({
          hist(rnorm(input$num), main = input$title)
  })
}
shinyApp(ui = ui, server = server)

When notified that the reactive function is invalid, the object created by a render*() function will rerun the entire block of code associated with it.

Example 2, two outputs

ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100),
  plotOutput("hist"),
  verbatimTextOutput("stats")
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num))
  })
  output$stats <- renderPrint({
    summary(rnorm(input$num))
  })
}
shinyApp(ui = ui, server = server)

Problem: input$num for hist and stats are not the same.

Example 2, two outputs

reactive() function builds a reactive object - will make sure the same input for all downstream functions

data <- reactive( {rnorm(input$num)})

Example 3, reactive

ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100),
  plotOutput("hist"),
  verbatimTextOutput("stats")
)
server <- function(input, output) {
  data <- reactive({
    rnorm(input$num)
  })
  output$hist <- renderPlot({
    hist(data())
  })
  output$stats <- renderPrint({
    summary(data())
  })
}
shinyApp(ui = ui, server = server)

Prevent auto update – isolate()

isolate({rnorm(input$num)})
ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100),
  textInput(inputId = "title",
            label = "Write a title",
            value = "Histogram of Random Normal Values"),
  plotOutput("hist")
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num),
         main = isolate({input$title}))
  })
}
shinyApp(ui = ui, server = server)

Trigger code – observeEvent()

Example 5, actionButton

library(shiny)
ui <- fluidPage(
  actionButton(inputId = "clicks",
               label = "Click me")
)
server <- function(input, output) {
  observeEvent(input$clicks, {
    print(as.numeric(input$clicks))
  })
}
shinyApp(ui = ui, server = server)

Delay reactions – eventReactive()

Example 6, eventReactive()

ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number",
              value = 25, min = 1, max = 100),
  actionButton(inputId = "go",
               label = "Update"),
  plotOutput("hist")
)
server <- function(input, output) {
  data <- eventReactive(input$go, {
    rnorm(input$num)
  })
  output$hist <- renderPlot({
    hist(data())
  })
}
shinyApp(

compare eventReactive with observeEvent

They are identical but for one thing:

Manage state – reactiveValues()

Example 7 – reactiveValues()

ui <- fluidPage(
  actionButton(inputId = "norm", label = "Normal"),
  actionButton(inputId = "unif", label = "Uniform"),
  plotOutput("hist")
)
server <- function(input, output) {
  rv <- reactiveValues()
  rv$data <- rnorm(100)
  observeEvent(input$norm, { rv$data <- rnorm(100) })
  observeEvent(input$unif, { rv$data <- runif(100) })
  output$hist <- renderPlot({
    hist(rv$data)
  })
}
shinyApp(ui = ui, server = server)

Summarize

More on UI (user interface)

The R Shiny UI was built by HTML, try the following in R console:

library(shiny)
ui <- fluidPage()
ui
sliderInput(inputId = "num",
  label = "Choose a number",
  value = 25, min = 1, max = 100)
plotOutput("hist")

Basic html code

<div class="container-fluid">
  <h1>My Shiny App</h1>
  <p style="font-family:Impact">
    See other apps in the
    <a href="http://www.rstudio.com/products/shiny/shiny-usershowcase/">
    Shiny Showcase</a>
  </p>
</div>

Save it to a html file, and open it.

When writing HTML, add content with tags:

<h1></h1>
<a></a>

How to add content to a web page in R Shiny

When writing R, add content with tag functions

names(tags)
##   [1] "a"                   "abbr"                "address"            
##   [4] "animate"             "animateMotion"       "animateTransform"   
##   [7] "area"                "article"             "aside"              
##  [10] "audio"               "b"                   "base"               
##  [13] "bdi"                 "bdo"                 "blockquote"         
##  [16] "body"                "br"                  "button"             
##  [19] "canvas"              "caption"             "circle"             
##  [22] "cite"                "clipPath"            "code"               
##  [25] "col"                 "colgroup"            "color-profile"      
##  [28] "command"             "data"                "datalist"           
##  [31] "dd"                  "defs"                "del"                
##  [34] "desc"                "details"             "dfn"                
##  [37] "dialog"              "discard"             "div"                
##  [40] "dl"                  "dt"                  "ellipse"            
##  [43] "em"                  "embed"               "eventsource"        
##  [46] "feBlend"             "feColorMatrix"       "feComponentTransfer"
##  [49] "feComposite"         "feConvolveMatrix"    "feDiffuseLighting"  
##  [52] "feDisplacementMap"   "feDistantLight"      "feDropShadow"       
##  [55] "feFlood"             "feFuncA"             "feFuncB"            
##  [58] "feFuncG"             "feFuncR"             "feGaussianBlur"     
##  [61] "feImage"             "feMerge"             "feMergeNode"        
##  [64] "feMorphology"        "feOffset"            "fePointLight"       
##  [67] "feSpecularLighting"  "feSpotLight"         "feTile"             
##  [70] "feTurbulence"        "fieldset"            "figcaption"         
##  [73] "figure"              "filter"              "footer"             
##  [76] "foreignObject"       "form"                "g"                  
##  [79] "h1"                  "h2"                  "h3"                 
##  [82] "h4"                  "h5"                  "h6"                 
##  [85] "hatch"               "hatchpath"           "head"               
##  [88] "header"              "hgroup"              "hr"                 
##  [91] "html"                "i"                   "iframe"             
##  [94] "image"               "img"                 "input"              
##  [97] "ins"                 "kbd"                 "keygen"             
## [100] "label"               "legend"              "li"                 
## [103] "line"                "linearGradient"      "link"               
## [106] "main"                "map"                 "mark"               
## [109] "marker"              "mask"                "menu"               
## [112] "meta"                "metadata"            "meter"              
## [115] "mpath"               "nav"                 "noscript"           
## [118] "object"              "ol"                  "optgroup"           
## [121] "option"              "output"              "p"                  
## [124] "param"               "path"                "pattern"            
## [127] "picture"             "polygon"             "polyline"           
## [130] "pre"                 "progress"            "q"                  
## [133] "radialGradient"      "rb"                  "rect"               
## [136] "rp"                  "rt"                  "rtc"                
## [139] "ruby"                "s"                   "samp"               
## [142] "script"              "section"             "select"             
## [145] "set"                 "slot"                "small"              
## [148] "solidcolor"          "source"              "span"               
## [151] "stop"                "strong"              "style"              
## [154] "sub"                 "summary"             "sup"                
## [157] "svg"                 "switch"              "symbol"             
## [160] "table"               "tbody"               "td"                 
## [163] "template"            "text"                "textarea"           
## [166] "textPath"            "tfoot"               "th"                 
## [169] "thead"               "time"                "title"              
## [172] "tr"                  "track"               "tspan"              
## [175] "u"                   "ul"                  "use"                
## [178] "var"                 "video"               "view"               
## [181] "wbr"

tags is a list of functions

tags$h1
tags$h1()
tags$h1("this is a header")
tags$a(href = "www.rstudio.com", "RStudio")
ui <- fluidPage(
  tags$h1("this is a header"), 
  tags$a(href = "www.rstudio.com", "RStudio")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

h1() - h6()

ui <- fluidPage(
  tags$h1("First level"),
  tags$h2("Second level"),
  tags$h3("Third level"),
  tags$h4("Fourth level"),
  tags$h5("Fifth level"),
  tags$h6("Sixth level")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

text and paragraph

ui <- fluidPage(
 "This is a Shiny app.",
  "It is also a web page."
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)
ui <- fluidPage(
  tags$p("This is a Shiny app."),
  tags$p("It is also a web page.")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

styles

ui <- fluidPage(
 tags$em("This is a Shiny app."), 
 tags$br(),
 tags$strong("This is a Shiny app."),
 tags$hr(),
 tags$code("This is a Shiny app."), 
 tags$p("This is a", tags$strong("Shiny"), "app."),
 tags$img(height=100, width=100, src="https://caleb-huo.github.io/teaching/2022FALL/logo.png")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Adding images from local file

To add images from local files, save the file in a subdirectory named - www

Summary of common tags

Raw HTML code

Use HTML() to pass a character string as raw HTML

ui <- fluidPage(
 HTML("<h1>My Shiny App</h1>")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

More reference on HTML UI:

shiny.rstudio.com/articles/html-ui.html

layout functions

layout functions examples

ui <- fluidPage(
  fluidRow(
    column(3),
    column(5, 
           sliderInput(inputId = "num",label = "Choose a number",
                       value = 25, min = 1, max = 100)
    )
  ),
  fluidRow(
    column(4, offset = 8,
           plotOutput(outputId="hist")
    )
  )
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num))
  })
}
shinyApp(ui = ui, server = server)

Panels

Panels to group multiple elements into a single unit with its own properties.

ui <- fluidPage(
  #wellPanel(
  sliderInput(inputId = "num",label = "Choose a number",
              value = 25, min = 1, max = 100),
  textInput("title", value = "Histogram", label = "Write a title"),
  #)
  plotOutput(outputId="hist")
)
server <- function(input, output) {
  output$hist <- renderPlot({
          hist(rnorm(input$num), main = input$title)
  })
}
shinyApp(ui = ui, server = server)

More on panels

tabsetPanel()

ui <- fluidPage(
  tabsetPanel(
    tabPanel("tab 1", "contents"),
    tabPanel("tab 2", "contents"),
    tabPanel("tab 3", "contents")
    )
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Prepackaged layout – sidebarLayout()

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(),
    mainPanel()
  )
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)
ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      sliderInput(inputId = "num",label = "Choose a number",
                  value = 25, min = 1, max = 100),
      textInput("title", value = "Histogram", label = "Write a title")
    ),
    mainPanel(
      plotOutput(outputId="hist")
    )
  )
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num), main = input$title)
  })
}
shinyApp(ui = ui, server = server)

Prepackaged layout – navbarPage()

ui <- navbarPage( title = "Title",
            tabPanel("tab 1", "contents"),
            tabPanel("tab 2", "contents"),
            tabPanel("tab 3", "contents")
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Prepackaged layout – navbarMenu()

ui <- navbarPage(title = "Title",
          tabPanel("tab 1", "contents"),
          tabPanel("tab 2", "contents"),
          navbarMenu(title = "More",
                tabPanel("tab 3", "contents"),
                tabPanel("tab 4", "contents"),
                tabPanel("tab 5", "contents")
          )
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Prepackaged layout – dashboardPage()

dashboardPage() comes in the shinydashboard package

library(shinydashboard)
ui <- dashboardPage(
          dashboardHeader(),
          dashboardSidebar(),
          dashboardBody()
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)