shiny
This quick tutorial will cover three main parts:
- The very basics: where we’re going to look at the structure of a shiny app.
- Deploying your app: A quick example on how to make your app public.
- How to do cool stuff: A list of tips on how to go further and use shiny for your actual project.
What you need before the tutorial?
Through this tutorial I’ll assume you have some basic R
and computer skills. What I mean by that is that you have a basic understanding of the following (i.e. you answer yes to the following questions):
- You have used a function in
R
. - You have used a
"list"
object inR
. - You have used the
<-
attribution function inR
. - You have installed and loaded packages in
R
. - You have changed your directory in
R
. - You have executed a script in
R
. - You have the following packages installe:
ape
,shiny
andrsconnect
install.packages(c("ape", "shiny", "rsconnect"))
library(shiny)
library(rsconnect)
The following points are useful but not essential:
- You have created a function in
R
. - You have used a shell script in a terminal.
Finally, things are made easier if you have an account on a collaborative version control server (GitHub, BitBucket, or other) and have an account on shiny.apps.io (it’s free and you only need an email address for that!).
Some useful scripts
If you’re using shell, you can download this scripts (right click + save as) for saving time. Don’t worry if you’re not using shell or you don’t like saving time, we’ll go through them manually below.
The very basics
A shiny app is wrapped into a folder that must contain at least these two files ui.R
and server.R
.
The ui.R
is the user interface. This is the file that contains all the graphical information you will see on the screen when running the app (the name, the different boxes, the results of the plots, etc…). You can think of it of the “screen” of the app, or the rendering of a Rmarkdown file. Actually scratch that, these analogies are not really good because there is another important aspect of the ui.R
file is that it also contains all the possible interactions with the interface and the app. So if it’s a screen it’s more like a touch screen. Basically the user interface is… The interface between the user and the app.
The server.R
is the “application” part of the app. This is the script that is executed whenever you make changes to the graphical interface. Basically if you request something from the user interface, the ui.R
calls to the server.R
file to do the right things. This will become more apparent and more logical once we have something running.
The very beginning
We’ll first begin by making the most useful app ever: when executed it prints the title of the app on your screen (wow!).
You can start by writting the following to your ui.R
file:
#################################
## The user interface (ui.R) file
#################################
## Define the user interface of the app
ui <- fluidPage(
## App title
titlePanel("ApeApp")
)
Note the function like structure here: we attribute the results of the function shiny::fluidPage
to ui
. And this function takes as argument titlePanel("ApeApp")
(which is the shiny::titlePanel
function that intakes the "ApeApp"
argument). You’re probably more familiar with functions written in one line (ui <- fluidPage(titlePanel("ApeApp"))
) that is equally valid but it’s gonna be unreadable really quickly, hence this layout.
And for the server.R
function we are going to make a empty app:
#################################
## The server (server.R) file
#################################
## Defining a function that does nothing
server <- function(input, output) {
}
Here we are creating a function that we call server
and that takes the arguments input
and output
(these arguments, like the naming server
and ui
are mandatory if you want your app to compile). This function doesn’t do anything but we’ll fill it up later.
We can now execute our app through shiny (you need to be in the same directory as your ui.R
and server.R
file):
## Loading shiny
library(shiny)
## Running the app
runApp(".")
Wowowowowow! Look at that! OK, this example is ridiculously simple. We can make it slightly more advanced. For the rest of this basic tutorial we’re going to make a small app that generates a random tree (with the ape::rtree
function) with variable number of tips and plots it.
For that we are going to:
- Update the interface
ui.R
to allow generate an input variable (the number of tips) and to plot an output (the resulting tree plot). - Update the
server.R
to allow to simulate a tree with a variable number of tips (the input) and generating a plot of that tree (the output).
It is important to understand the dynamic of the ui.R
generating input for the server.R
and the server.R
generating output for the ui.R
.
Let’s first modify the ui.R
:
## Define the user interface of the app
ui <- fluidPage(
## App title
titlePanel("ApeApp"), ## Note this comma here!
## This is like in a normal function
## to separate argument. You'll easily
## forget it so remember to always check
## it for debugging your app!
## Sidebar layout with input and output definitions
sidebarLayout(
## Sidebar panel for inputs
sidebarPanel(
## Input: A Slider for the number of tips
sliderInput(inputId = "tips", ## What the variable is called (input$tips)
label = "Number of tips:", ## What the user will see
min = 3, ## The bounds of the slider
max = 100, ## The bounds of the slider
value = 30) ## The defaut value of the slider
),
## Main panel for displaying outputs
mainPanel(
## Output: A phylo plot
plotOutput(outputId = "treeplot")
)
)
)
Here we’ve added a lot of new arguments that I’m not going to detail much here. Basically each creates a different element of the interface, whether it is display panels (shiny::sidebarLayout
, shiny::mainPanel
, etc.), input widgets (sliderInput
) or output handlers (plotOutput
). I’ll talk more about how to navigate through them in the advanced part. If you’re curious now, you can always look at them yourself by looking at their manuals (e.g. ?shiny::plotOutput
).
Note again, the layout of the script here. You could write it: ui <- fluidPage(titlePanel("ApeApp"), sidebarLayout(sidebarPanel(sliderInput(inputId = "tips", label = "Number of tips:", min = 3, max = 100, value = 30)), mainPanel(plotOutput(outputId = "treeplot"))))
but only do that if you actively like bleeding from your eyes.
The important thing here is to see what are the input and what are the outputs (here the input is the tips generated by the slider and the output is the plot).
Even though the server.R
will not react (it doesn’t know what to do with inputs and outputs yet), you can visualise the app using runApp(".")
and see that satisfying slider!
OK, so now let’s handle the input and generate the output in the server.R
:
#################################
## The server (server.R) file
#################################
## Loading the packages required
library(ape)
## Define server logic required to simulate and plot the tree
server <- function(input, output) {
## Simulate and plot a tree and save it in output$treeplot
output$treeplot <- renderPlot({
## Simulate a tree based on an input: the number of tips (input$tips)
tree <- rtree(input$tips)
## Plotting the tree
plot(tree)
})
}
So here the function server
that intakes input
and output
(they are actually of class "list"
) will internally run the shiny::renderPlot
function and store these results in output$treeplot
. Within that function, the ape::rtree
function takes as input the variable number of tips that can be accessed by input$tips
(determined by the slider we created in ui.R
). The output of the server (output$treeplot
) is then fed to the ui.R
file that knows what to do with it (“plotOutput(outputId = "treeplot")
”).
Et voilà. You can now run the app using runApp(".")
again and it should be nice and interactive.
From there, of course there are many more things to make the app nice but we’ll cover that in “How to do cool stuff” later on.
Deploying your app
First let see how to deploy this amazing app to make it publically available so that everyone can bask in these glorious randomly generated phylogenies (thanks to the ape
, shiny
and R
teams…).
This step is actually rather straightforward and explained in details on the shiny.app.io
main page once you create an account and first login. Briefly you need to:
- Install and load
rsconnect
install.packages("rsconnect")
- Use the token generated by
shiny.app.io
to syncrsconnect
to your account
rsconnect::setAccountInfo(name = ... ,
token = ... ,
secret = ...)
The name
, token
and secret
are generated by shiny.app.io
so just copy paste their instructions and numbers.
- Deploy the app for the first time
library(rsconnect)
rsconnect::deployApp("path/to/your/app")
Pro-ish tip: once you’ve deployed your app for the first time, it’ll create a
rsconnect
folder in your folder that contains all the info for linking you app toshiny.app.io
(DO NOT MODIFY!). If you’re familiar with shell scripts, you can use this script for deploying a shiny app to routinely update your app (without having to worry about numbers and stuff).
Note that deploying the app takes some time so just let it running in the background. In the mean time I’m going to answer your questions and show you my tips on how to create the coolest app (or not).
How to do cool stuff
These are tips for developing apps beyond the super basic examples and how I go for it. I’m by far not good at it but these are the tips I’ve learned while I developed apps and that I think would have come in handy when I started (they’re in no particular order).
Use version control. Seriously, that should apply to everything you do but it’s so obviously important here: your
ui.R
file will quickly reach thousands of lines of code and it is really easy to make mistakes that break your app entirely and you can spend ages trying to find the mistake (usually a misplaced comma or a duplicated variable name). With a good version control routine, you can easily go back to the version that used to work before you tried to add this cool new feature.Test your app. All the time. This goes with the version control tip (always version control and unit test your code, seriously!). Again because it’s easy to make mistakes it’s important to regularly check if you didn’t break anything. You can easily do that by regularly running
runApp(".")
and see if things break.Make sure you know what you want to achieve. For me shiny is really exciting and I can lose a decent amount of time implementing options that I won’t end up using (e.g. handling colour options but figuring out that I don’t need to plot things in the end). The best way to do that is probably to run your analysis in R normally before running it in shiny (i.e. write a script that makes your plot the way you want without shiny first).
Once you have your script neatly written. Clearly identify the inputs and the outputs. Basically make sure you have a good idea of what are the variables inputs you want (in our simplistic example, the input is the number of taxa but not the type of the tree process - check if that’s what you want or not). Same for the outputs (do you want only a plot? do you also want a table?, etc.).
Get inspired by the showcase examples but get code from the specific examples (or stack overflow). I find the showcase examples really inspiring for what can be done but since they have a lot of things going on and are not always written in a didactic way, I find using them rather complicated. On the other hand the specific examples are made especially for the helping you. You can see them as tutorials for specific snippets.
Essential links
The
shiny
manual: https://shiny.rstudio.com/articles/Specific examples: https://shiny.rstudio.com/gallery/#demos
Inspiration examples: https://shiny.rstudio.com/gallery/#user-showcase
The
shinyapps.io
manual: https://docs.rstudio.com/shinyapps.io/
Thomas Guillerme WORKSHOP
R package data