An Introduction to Shiny

Hands-on workshop

Andreas M. Brandmaier and Leonie Hagitte

7/1/23

Welcome

Ask questions anytime

This is us

Andreas (find me on Twitter)

Leonie (find me on LinkedIn)

Shiny

Shiny is an R package that makes it easy to build interactive web apps in R

Apps can be

-standalone, - deployed to a website, - or be part of an interactive (Markdown) document

Required software

You need to install these software packages in order to follow along with the examples of today:

  • R: https://cran.r-project.org
  • RStudio: https://rstudio.com/products/rstudio/download/
  • shiny
  • tidyverse packages (and some others)
install.packages(c("shiny","tidyverse", "ggraph","palmerpenguins"))

What to expect

  • This is a hands-on workshop; you’ll get the most out of it if you download the materials and actively participate
  • Introductory R coding skills are OK! We have exercises at varying levels of proficiency
  • The workshop materials remain open and accessible after the workshop
  • Feel free to team up! ]

an image

Goals

Objectives of today

  • Learn about the structure of a shiny application.

  • Learn how to create shiny apps from a template.

  • Learn how to think in terms of inputs and outputs.

  • Write your own app (using simulated data, real data or your data)

Content

Let’s talk about…

  • User-interface / Layout
  • Reactivity / Logic
  • Awesome visualizations

Anatomy of a Shiny app

library(shiny)

shinyApp(
  ui = list(),
  server = function(input, output, session) {  }
)
shinyApp(ui, server)

The ui specifies the visible user interface

  • Dynamic elements inputs and outputs
  • Static elements like headings, text, static images
  • A layout how to arrange these things

Anatomy of a Shiny app

library(shiny)

shinyApp(
  ui = list(),
  server = function(input, output, session) {  }
)
shinyApp(ui, server)

The server is invisible and is responsible for all computations

  • The server monitors inputs
  • When inputs change, outputs are updated (reactivity)

User-interface

Example

Inputs have unique ids that correspond to server-side variables, a label, a starting value and extra options (e.g., range restrictions, etc.)

textInput(inputId="familyname", label="Family name:", value="Steve Miller" )

or

numericInput(inputId="age", label="Age (in years):", value=1, min=0, max=150 )

On the server, we will be able to access variables input$familyname and input$age

Layout

Multi-row layout

Other layouts

Many more, e.g. Tabsets - see tabsetPanel()

Outputs

Example output elements (placeholders for dynamic content):

  • textOutput()

  • plotOutput()

  • tableOutput()

You can use

help.search("Output", package = "shiny")

to find other output functions in shiny.

Outputs and Renderers

Each *Output() function has a corresponding render*() server-side function. For example:

  • textOutput() \(\rightarrow\) renderText()
  • plotOutput() \(\rightarrow\) renderPlot()
  • tableOutput() \(\rightarrow\) renderTable()

Server logic: Accessing inputs

shinyApp(
  ui = list(),
  server = function(input, output, session) {  }
)
  • Inputs are accessed in the server function via the input argument.

  • Inputs are reactive, meaning that changes trigger updates to outputs.

Example: A pocket calculator

Demo 1 - Plus One

Demo: We write a simple calculator that adds +1 to a number we enter.

The simplest structure of a reactive program involves just a source and an endpoint:

flowchart LR
  subgraph outputs
  re([result])
  end
  subgraph inputs
  n1([number]) 
  end
  n1 --> re

Demo 1 - Plus One

demos/demo1.R

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(
  
  # Application title
  titlePanel("Calculator"),
  
  # Sidebar with a slider input for number of bins 
  sidebarLayout(
    sidebarPanel(
      numericInput("number",
                   "Number", value=0)
    ),
    
    # Show a plot of the generated distribution
    mainPanel(
      h3("Result"),
      textOutput("result")
    )
  )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
  
  output$result <- renderText({
    return(input$number + 1)
  })
}

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

Your turn - Exercise 1

Copy the code from the previous slide (or open exercises/ex1.R) and run it in R

Check that you are able successfully run the shiny app and are able to interact with it.

  • If everything is working try modifying the code (e.g. try adding a second number input and change the logic so that both numbers are added).

Reactive diagram

flowchart LR
  subgraph outputs
  re([result])
  end
  subgraph inputs
  n1([number1]) 
  n2([number2]) 
  end
  n1 --> re
  n2 --> re

Your Turn - Exercise 2

  • Continue with your code (or from XXX.R) and add a menu to choose different operators (e.g., plus, minus)

  • For example, add a selectInput(inputId, label, choices)

  • Add server-side logic to implement the different operators

##Reactive diagram

flowchart LR
  subgraph outputs
  re([result])
  end
  subgraph inputs
  n1([number1]) 
  n2([number2]) 
  op([operator])
  end
  n1 --> re
  n2 --> re
  operator --> re

Solution

challenges/01-calculator/app-solution.R

#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Calculator"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            numericInput("n1",
                        "Number", value=0),
            numericInput("n2",
                         "Number", value=0),
            selectInput("operation","Operator",c("+","-"))
        ),

        # Show a plot of the generated distribution
        mainPanel(
           textOutput("result")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

    output$result <- renderText({
        if (input$operation=="+")
        return(input$n1+input$n2)
      else
        return(input$n1-input$n2)
    })
}

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

Who doesn’t like penguins?

Palmer Penguins

We are going to use the penguins dataset from palmerpenguins

species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
Adelie Torgersen 39.1 18.7 181 3750 male 2007
Adelie Torgersen 39.5 17.4 186 3800 female 2007
Adelie Torgersen 40.3 18.0 195 3250 female 2007
Adelie Torgersen NA NA NA NA NA 2007
Adelie Torgersen 36.7 19.3 193 3450 female 2007
Adelie Torgersen 39.3 20.6 190 3650 male 2007

Reactive expression

demos/demo2.R

library(shiny)
library(tidyverse)
library(palmerpenguins)

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Penguins"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            # <-------   here go input elements
        ),

        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("plot1"),
           plotOutput("plot2"),
           textOutput("text1")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

    output$plot1 <- renderPlot({
       penguins %>% ggplot(aes(x=body_mass_g,y=bill_length_mm))+
        geom_point()+
        geom_smooth(method = "lm")
    })
    
    output$plot2 <- renderPlot({
      penguins %>% ggplot(aes(x=flipper_length_mm))+geom_histogram()
    })
    
    output$text1 <- renderText({
      paste0("There are ",nrow(penguins), " penguins in the data set")
    })
}

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

Reactive diagram

flowchart LR
  subgraph outputs
  pl1([plot1])
  pl2([plot2])
  tx1([text1])
  end
  subgraph inputs
  slider1([slider1]) 
  n1([number1]) 
  rn1([range1])
  ck1([ck1])
  end

Your Turn - Exercise 3

Copy the code from the previous slide (or open exercises/XXX.R) and run it in R

Add logic to create a second plot as output plot2 on the server

Add extra features to make the diagrams interactive with additional inputs (e.g., add a selectInput for subgroup selection of penguin species) or add a rangeInput to display only certain ranges of years

DRY - Don’t repeat yourself

Assume a range input (sliderInput(value=c(0,10))) that filters data

Filter logic should be executed only once for every relevant output

Never copy&paste server logic, instead use a reactive element (here: compute):

flowchart LR
  subgraph outputs
  pl1([plot1])
  pl2([plot2])
  tx1([text1])
  end
  comp([compute])
  subgraph inputs
  slider1([slider1]) 
  n1([number1]) 
  rn1([range1])
  ck1([ck1])
  end
  compute --> pl1
  compute --> pl2
  rn1 --> compute
  n1 --> pl1