Make a web app with Shiny
25 Apr 2024Today we will learn how to make and deploy interactive web applications with Shiny. Shiny allows you to display your R analysis on the web to anyone. For example:
- I used Shiny to write an app to visualize a Markov Chain simulation of genetic drift.
- A student from the 2014 BIS180L worked in my lab to start a visualizer for QTL data and gene expression.
- The Shiny website has plenty of additional examples.
The tutorial at Rstudio is good but pretty involved. I have tried to distill some of the principles below; if you like it and want more then I highly recommend the tutorial.
Resources
The cheat sheet may be helpful.
Components of a Shiny App
A ShinyApp has two main components components:
- ui This controls the user interface (i.e. the design of the webpage, the input and the output).
- server This does the work of performing any analysis, creating graphs, and creating tables
These components can:
- be saved in two separate files,
ui.R
andserver.R
- OR combined into a single file,
app.R
.
For this class we will use the single-file version.
Anatomy of a single file Shiny App
A skeleton shiny app will looks like this:
library(shiny) #always required
library(tidyverse) # additional libraries if needed by your script
# load data
dat <- read_csv("path/to/my/cool/data")
# Preprocessing
# Any one-time data preprocessing code should go here
ui <- fluidPage(
# Code to create the user interface goes here
)
server <- function(input, output) { # arguments named 'input' and 'output' are required.
# Code to create plots and or tables based on user input goes here
}
# Run the application
shinyApp(ui = ui, server = server)
Now let’s look more closely at the ui and server components
ui
Here is a sample ui
object. This script makes use of R’s built in data set on iris. For more information on this data set you can type ?iris
in R.
# Define UI for application that draws a histogram
ui <- fluidPage( #create the overall page
# Application title
titlePanel("Iris Data"),
# Some helpful information
helpText("This application creates a boxplot to show difference between",
"iris species. Please use the radio box below to choose a trait",
"for plotting"),
# Sidebar with a radio box to input which trait will be plotted
sidebarLayout(
sidebarPanel(
radioButtons("trait", #the input variable that the value will go into
"Choose a trait to display:",
c("Sepal.Length",
"Sepal.Width",
"Petal.Length",
"Petal.Width")
)),
# Show a plot of the generated distribution
mainPanel(
plotOutput("boxPlot")
)
)
)
There are several components in the above code (and note that they are nested)
fluidPage
This creates the layout for our webpage. The webpage has three components:titlePanel
Should be obvioushelpText
Should be obvioussidebarLayout
Creates a sidebar layout within fluidpage.sidebarPanel
Specifies the sidebar panel.radioButtons
Specifies that we want radio buttons in this sidebar panel. We could have additional input functions here.
mainPanel
Specifies that we want a main panel as well.plotOutput
What we want on the main panel. We could have more than one of these, or could have tables, etc.
To see all the types of input and output that can be included, see the Shiny reference. Of particular interest:
tabPanel
If we want multiple tabs on the page.checkboxInput
sliderInput
tableOutput
textOutput
server
Below is an example of the server component to accompany the ui above
# Define server logic required to draw a boxplot
server <- function(input, output) {
# Expression that generates a boxplot. The expression is
# wrapped in a call to renderPlot to indicate that:
#
# 1) It is "reactive" and therefore should re-execute automatically
# when inputs change
# 2) Its output type is a plot
output$boxPlot <- renderPlot({
plotTrait <- as.name(input$trait) # convert string to name
# set up the plot
pl <- ggplot(data = iris,
aes(x=Species,
y= !! plotTrait, # !! to use the column names contained in plotTrait
fill=Species
)
)
# draw the boxplot for the specified trait
pl + geom_boxplot()
})
}
The key elements of the above code:
server <- function
We are creating a function calledserver
that will do analyses and create plots or tables for our app.renderPlot
This tells R that the enclosed code will generate a plot that is to be put on the webpage. Note that there are similar functions for outputting tables and text.
- Information is passed between the
ui
andserver
components via theinput
andoutput
variables.- In the
ui
component we specified that theradioButton()
information should be placed in “trait”.server
accesses that throughinput$trait
. - Similarly in the
server
component we specify that the rendered plot goes into output$boxPlot. Then inui
we can access the plot as “boxPlot”.
- In the
- Pay attention to the modifications in how variables are given in the
aes()
argument toggplot
. Because we wantaes()
to use the contents ofinput$trait
to select a column, we first have to convertinput$trait
to a name withas.name()
(placing the contents inplotTrait
) and then use!!
to tellaes()
to get the name out ofplotTrait
instead of just taking it literally as a column name. - Important any one-time data loading or manipulation (i.e. reading in a file) should be done BEFORE, not within, any
renderNNN
statements. You can put it at the beginning of the script.
Create a team repo
Before getting started:
- One person in each team should click the github assignment link, create a team, and create a repo.
- Other members of the team should then click on the github link and join that team.
- Everyone should clone the repo.
trying the demoApp
Clone the assignment repo. You will find two directories in the repo demoApp
and teamApp
demoApp
contains a working version of the app described above. In Rstudio, navigate into the demoApp
directory, and then click on app.R
.
Run the app by clicking on the runApp
button at the top of the script pane.
Exercise 1 (2 pts)
Work with your partner to:
Change the demoApp
script so that a violin plot is produced instead of a boxplot.
Exercise 2 (2 pts)
Work with your partner to:
Change the demoApp
script so that all of the traits are plotted as a violing plot but only for a single species. (The x-axis should be trait; the user selects species).
Push your changes.
Create a team app
Your team should work together to create and deploy a ShinyApp that plots some aspect of the data from the BLAST or RICE labs. The app should allow user input that modifies the plot in a useful way.
I have listed some ideas below, but feel free to choose something else
RICE data ideas
You might want to limit the user input to 5 or 10 traits in the examples below, just to save yourself some typing and to keep the radio button list not too long (or use selectize)
- Interactive plot showing histograms or violin plots or boxplots of user-selected phenotypic data split by ancestral population assignment or region. You could also allow the user to choose whether it was a histogram or a violin plot, etc.
- Scatter plot of any two traits (user chosen), colored by the values of a third (user chosen).
- If you want to get fancy in either of the above then you could use the selectize tool to allow the user to select from all of the possible phenotypes.
Other data ideas
- Feel free to use any data set that you are interested in; if you work in an lab maybe a data set from your lab work.
- You could use the tomato data set that was used in the ggplot tutorial and:
- explore the relationship between altitude and plant height
- plot trait averages per species letting the user choose the trait
- etc.
Scoring (out of 20 points)
4 points for the modifications to the demoApp (Exercises 1 and 2 above).
12 points for a functional, interactive web app deployed on shinyapps.io and pushed to GitHub.
+ 2 points for using two or more input widget types (slider and radio buttons, for example).
+ 2 points for good annotation on the web page (a new user would understand what the app is about).
- 2 or more points for each student that does not make at least 2 substantive commits to the team repository.
Getting started with the Team App
- Discuss your goals and what you want your final product to look like with your team members.
- There is a skeleton
app.R
inside theteamApp
directory in your repo. Build your app there. - Start simple.
- Remember that you can use git commit and push and pull to share your file changes among the team. I recommend that you do not have two people work on the same section of the same file at once; it makes it hard to do automatic merge when you pull.
- Once you get it running, sign up for an account and deploy it on shiny.io. See deployment, below.
- Place a link to your shiny web app in the README.md in your github repository.
- List the students that contributed in your README.md in your github repository.
Going further
To get further inspiration, checkout out the Shiny Gallery.
You can also include Shiny apps directly in your Rmarkdown files to be rendered into html. In some ways the syntax is even easier because you only need a single file. You can also include interactive graphs in slide presentations this way.
Deployment
Once you have an awesome application how do you share it?
For this lab, we will use Rstudio’s free shiny server.
To use their server (required to complete the assignment, but wait to do these steps until you have your app working)
- One person in your group needs to go to the shiny io website and register for an account. You can use your github or google ID.
- After registering go to account > tokens. Click on “show”, Click on “Show Secret”, and then “copy to clipboard”
- Paste it into the R command below and run it. It should look something like:
rsconnect::setAccountInfo(name='jnmaloof', token='45515FE2BB923C41A95D9768C9AD6F91', secret='somelonggibberishheredonotshare')
- This only needs to be done once per computer.
Once you have signed up for an account and authenticated it a simple as:
library(rsconnect)
deployApp('path/to/your/app')
The above process is also described in the Shinyio user guide if would like more details.
You can see my version here.
Other ways to deploy (NOT NEEDED FOR BIS180L)
Now that we have our awesome application how do we share it?
Multiple options:
- If you are sharing it with someone that uses R and has the shiny library installed, then you can just send it to them, they can download it, and run it as above.
-
If you have it on GitHub and the person you want to share it with has R they can use:
library(shiny) runGitHub(repo = "HamiltonDemos",username = "jnmaloof", subdir = "BinomialDrift")
- You can use Rstudio’s free shiny server.
(see above)
- If you are advanced you can run your own server (I actually set up a server for my lab–it isn’t that hard).