Content
Let’s talk about…
- User-interface / Layout
- Reactivity / Logic
- Awesome visualizations
Hands-on workshop
7/1/23
Andreas (find me on Twitter)
Leonie (find me on LinkedIn)
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
You need to install these software packages in order to follow along with the examples 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)
Let’s talk about…
The ui
specifies the visible user interface
The server
is invisible and is responsible for all computations
server
monitors inputsInputs 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
Many more, e.g. Tabsets - see tabsetPanel()
Example output elements (placeholders for dynamic content):
textOutput()
plotOutput()
tableOutput()
You can use
to find other output functions in shiny.
Each *Output()
function has a corresponding render*()
server-side function. For example:
textOutput()
\(\rightarrow\) renderText()
plotOutput()
\(\rightarrow\) renderPlot()
tableOutput()
\(\rightarrow\) renderTable()
Inputs are accessed in the server function via the input argument.
Inputs are reactive, meaning that changes trigger updates to outputs.
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
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)
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.
flowchart LR subgraph outputs re([result]) end subgraph inputs n1([number1]) n2([number2]) end n1 --> re n2 --> re
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
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)
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 |
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)
flowchart LR subgraph outputs pl1([plot1]) pl2([plot2]) tx1([text1]) end subgraph inputs slider1([slider1]) n1([number1]) rn1([range1]) ck1([ck1]) end
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
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