You will need the following R packages to run this tutorial
install.packages("shiny")
install.packages("tidyverse")
install.packages("plotly")
source("http://www.bioconductor.org/biocLite.R")
biocLite("breastCancerNKI")
Create a new app by the following menu options in RStudio. When prompted for a name of the app, call it test
or something.
File -> New File -> Shiny web app
A file called app.R
should appear in RStudio, and RStudio will have created a new directory with the app name that you specified (e.g. test
). The app.R
script defines the UI (user interface), code to be run by the server and ends with an instruction to run the app. Note that you might see some older Shiny apps that were defined in two separate files (UI.R
and server.R
). If you view this file in RStudio, the option to run the app will appear in the top toolbar.
Hitting the Run App Button will create a new RStudio window this example app. Congratulations, you have just created your first shiny app 🎉
You could also view in a web browser. Please note that when presented “live” the Shiny app will appear below. On the static HTML pages linked from the course website, there will be grey box below.
In this example app we are creating a histogram of a pre-defined dataset (faithful
), with the user being able to specify how many bins to use in the histogram. Let’s first look at the part of the script that defines the interface.
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
sidebarPanel
and mainPanel
arguments respectivelybins
variable, and this value can be accessed from within in the server
function (see later) in order to re-draw the plot.distPlot
.HTML elements can be added by a number of functions that parallel common HTML tags. e.g. h1
, h2
for headers and a
to create a hyperlink. The helpText
function can also be used to add explanatory text below a particular input option. In the following code we add headers, some help text and a hyperlink to a reference about histograms. Images can also be added using the img
function.
You can try copy-and-pasting this code over the default sidebarPanel
function call in the app.R
file you have created.
######
sidebarPanel(
h1("Data Exploration Options"),
h2("The histogram"),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
helpText("This slider defines the number of bins to divide the data into"),
a("More about the histogram here", href="https://en.wikipedia.org/wiki/Histogram"),
br(),
p("App developed by University of Cambridge"),
img(src="http://www.cruk.cam.ac.uk/sites/all/themes/cambridge_theme/images/interface/main-logo-small.png")
),
######
This histogram example uses the sliderInput
function which allows the user to select a numeric value for the number of bins to allocate the data to between some lower and upper bound. We also have control over the amount by which the values change when the user slides the bar with step
, and the text label displayed above the bar. (See ?sliderInput
for details).
Other types of input are available for us to choose from and follow a similar syntax. i.e. each type of input has a value
and label
argument
selectInput
checkboxInput
radioButons
textInput
dateInput
fileInput
To add an extra input option, we need to add it as an argument to the sidebarPanel
function. We could add a option to colour the histogram using the radioButton
function. The order of the arguments to sidebarPanel
dictates the order in which the various elements are rendered in the HTML.
Try copying-and-pasting this code into your app.R
file
######
sidebarPanel(
radioButtons("colour","Colour of histogram",choices=c("red","green","blue"),selected="red"),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
######
You should see a set of radio buttons appear in the side panel. However, selecting a different option has no effect on the histogram. To do this we need to modify the code in the server
section of our app.R
script. This part of the script defines how each of the outputs that appear in the main panel are generated.
In this example, we just have a histogram of the faithful
dataset. In the UI part of the script we have defined a plot called distPlot
.
mainPanel(
plotOutput("distPlot")
)
The server script must specify how distPlot
is created and assign it to the list of outputs.
distPlot
is defined to be the result of renderPlot
input$bins
bins
Note that we use “base” graphics to plot the histogram, but shiny
is equally happy to use ggplot2
, lattice
, or whatever your favourite way of plotting is. The plot itself is static, but you can incorporate interactive graphs as we will see later.
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
To modify the colour according to what the user has selected, we change the col
argument in the hist
function from the original darkgray
to input$colour
.
# Try replacing the server function in your app.R and see what happens
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
#Now change the colour of the histogram too
hist(x, breaks = bins, col = input$colour, border = 'white')
})
}
In this example, the value of input$colour
is passed directly to the plotting function. This works because red
, green
, blue
are valid colours. We could change the values displayed in the drop-down to be more human-readable, but need to make sure they get transformed appropriately. Changing the configuration of the radioButtons
to the following wouldn’t work.
radioButtons("colour","Histogram Colour",choices=c("I want Red","I want Green","I want Blue"),selected="I want Red"),
However, we can access the value of input$colour
and create a new variable accordingly. For example we could have some kind of control structure
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
if(input$colour == "I want Red"){
mycol <- "red"
} else if (input$colour == "I want Green"){
mycol <- "green"
} else mycol <- "blue"
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = mycol, border = 'white')
})
}
textInput
main
argument to the hist
function lets you set the plot title.selectInput
) to allow the user to choose from any of the pre-defined colours in R. (HINT: the function colours
returns the names of all valid colours as a vector)numericInput
boxes to allow the user to define a colour using its red, green and blue values.
rgb
function will convert a set of numeric values into a hexademical string that can be used as an argument for plotting.255,0,0
We can add new outputs by adding new arguments to mainPanel
. Let’s say we want to show a boxplot of the data in addition to the histogram. First we add a call to plotOutput
in the mainPanel
code with our choice of object name. Let’s call it boxPlot
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
plotOutput("boxPlot")
)
)
)
We now need to modify the server code to create an object ouput$boxPlot
using the renderPlot
function. Inside the body of renderPlot
we write the code to create the boxplot.
However, the following server
code does not create a working app. Can you work out why? 🤔
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = input$colour, border = 'white',main=input$title)
})
output$boxPlot <- renderPlot({
boxplot(x,horizontal=TRUE,col=input$colour)
})
}
Aside from a plot, other types of output include
renderText
renderPrint
renderDataTable
renderTable
We will add a numerical summary of our dataset and an interactive table of the raw data values. This elements can be added to the mainPanel
using verbatimTextOutput
type
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
verbatimTextOutput("summary"),
dataTableOutput("data")
)
In the server
function we include the code to compute the relevant outputs. As part of the server
function definition we can also include code to load required packages and datasets. In order to overcome the error observed above we can define the variable x
that is used in plotting at the start of the server
code. This line of code will only get run once each when the server is started.
x <- faithful[,2]
server <- function(input, output) {
x <- faithful[, 2]
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = input$colour, border = 'white')
})
output$boxPlot <- renderPlot({
boxplot(x,col=input$colour)
})
output$summary <- renderPrint({
summary(x)
})
}
The results panel is now getting a bit cluttered. If we wish we could change the layout so each distinct output is displayed in a different panel. This can be done with the tabsetPanel
function. We can also add the option to view the data in an interactive table with dataTableOutput
.
mainPanel(
tabsetPanel(
tabPanel("Data", dataTableOutput("data")),
tabPanel("Summary", verbatimTextOutput("summary")),
tabPanel("Histogram",plotOutput("distPlot")),
tabPanel("Boxplot", plotOutput("boxPlot"))
)
)
The final code for the app is given below:-
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
radioButtons("colour","Colour of histogram",choices=c("red","green","blue"),selected="red"),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
tabPanel("Data", dataTableOutput("data")),
tabPanel("Histogram",plotOutput("distPlot")),
tabPanel("Summary", verbatimTextOutput("summary")),
tabPanel("Boxplot", plotOutput("boxPlot"))
)
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
x <- faithful[, 2]
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = input$colour, border = 'white',main=input$title)
})
output$summary <- renderPrint({
summary(x)
})
output$boxPlot <- renderPlot({
boxplot(x,horizontal = TRUE,col=input$colour)
})
output$data <- renderDataTable(faithful)
}
# Run the application
shinyApp(ui = ui, server = server)