What is Shiny?
The best way to have an idea of the possibilities offered by R/Shiny is to have a quick look at the following galleries (where you can find, and study, the associated R code!):
Explore now for a few minutes these galleries:
Note: Another way is to use the runExample()
function. The Shiny package has eleven built-in examples that each demonstrate how Shiny works. Each example is a self-contained Shiny app. For example
library(shiny)
runExample("01_hello") # a histogram
runExample("02_text") # tables and data frames
runExample("03_reactivity") # a reactive expression
runExample("04_mpg") # global variables
runExample("05_sliders") # slider bars
runExample("06_tabsets") # tabbed panels
runExample("07_widgets") # help text and submit buttons
runExample("08_html") # Shiny app built from HTML
runExample("09_upload") # file upload wizard
runExample("10_download") # file download wizard
runExample("11_timer") # an automated timer
Due credits: in this course, we will explore material that can be found at these URL:
You need a good knowledge about the R software: try this quiz to check your skills. If you want to improve your basic R skill, see for instance this book.
You also need a good knowledge about ggplot2
. Download the Data Visualization Cheat Sheet.
Some basic knowledge about HTML, Javascript and CSS might help.
The Shiny (client) software can run on Windows/MacOS/Linux. Works best with Chrome.
If not already done, download and install the last version of RStudio, which appears to be 1.0.136 at the date this course is prepared (This information is found from RStudio via the menu Help/About RStudio.)
Also, from Rstudio:
install.packages("shiny")
Download the Shiny Cheat Sheet.
Launch RStudio, then go to File/New File/Shiny Web App…
The following window pops up.
Change the entries as follows:
When you click on Create, this should open two new tabs in RStudio: ui.R and server.R. The corresponding files are stored in ~/My_First_Shiny_App. They already contain some R code that we will comment (and modify) in a while.
But, first let’s execute this code to see what happens. Select the ui.R (or the server.R) tab in RStudio. Then click on the button. This should launch a web app in your favorite web browser. You can then interact with a slider to choose the number of bins to use in order to draw a histogram of the “Old Faithful Geyser Data”. Change the slider value and observe how the histogram is redrawn.
Note 1: You should also see in the console of RStudio a red message displayed. On my laptop, it is: “Listening on http://127.0.0.1:3452”. This means that in the background, RStudio launches locally a Shiny daemon web server on the port 3452. (This port number might be different on your computer.) This can be checked for example under Linux by typing the following command in a terminal and noting that the STATE is “open”:
[lafaye 15:16:51 ~] nmap localhost -p 3452
Starting Nmap 6.47 ( http://nmap.org ) at 2016-12-28 15:17 CET
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000027s latency).
Other addresses for localhost (not scanned): 127.0.0.1
PORT STATE SERVICE
3452/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 0.05 seconds
Note 2: Your R session will be busy while the Hello Shiny app is active, so you will not be able to run any R commands. R is monitoring the app and executing the app’s reactions. To get your R session back, hit escape or click the stop sign icon (found in the upper right corner of the RStudio console panel).
First, note that a full description of all Shiny functions (e.g., the ones used in the code of our current versions of ui.R and server.R) is given at the Shiny reference pages. Have a look at this webpage to see how it is organized.
Some vocabulary: “A typical web application consists of a number of user interface (UI) elements, say a button or a checkbox. Each of these elements can be interacted with, for example you can push the button. This button push then triggers an action, running a piece of code. This can then change the state of the web application, for example retrieve a piece of information from the server or draw a picture in the application. This style of programming is called event-driven programming. The piece of code that is executed based on an event is called an event handler.”
“Shiny apps follow typical structure of web applications. However, as a user you only have to specify which UI elements you want to show, and the underlying R code that draws a plot, shows some text, or a table. These pieces of information are stored in two interacting codes (script) stored in the following files:
ui.R
, for the user interface (ui) elements, andserver.R
, for the content code."The ui.R
code creates the options a user can change, and controls what is displayed on the app.
input
) to store all optionsTher server.R
code runs everything that responds to the changes made by the user in the app.
Note: As of version 0.10.2, Shiny supports single-file applications. You no longer need to build separate server.R and ui.R files for your app; you can just create a file called app.R that contains both the server and UI components. You can learn more about building a Shiny app in a single file here, however this tutorial will focus on the two file structure for building a Shiny app.
As you can see, the ui.R
file starts (after some comments) with the instruction:
library(shiny)
that will load the shiny
package. Next, a call to the shinyUI()
function is made. This is not really necessary as can be read in the documentation of this function:
“Historically this function was used in ui.R files to register a user interface with Shiny. It is no longer required as of Shiny 0.10; simply ensure that the last expression to be returned from ui.R is a user interface. This function is kept for backwards compatibility with older applications. It returns the value that is passed to it.”
You can thus remove it. But leave the fluidPage
stuff.
The fluidPage()
function creates fluid page layouts. A fluid page layout consists of rows which in turn include columns. Rows exist for the purpose of making sure their elements appear on the same line (if the browser has adequate width). Columns exist for the purpose of defining how much horizontal space within a 12-unit wide grid its elements should occupy. Fluid pages scale their components in realtime to fill all available browser width.
Our current fluidPage contains two rows:
titlePanel()
function. This row only displays “Old Faithful Geyser Data”.sidebarLayout()
function. It contains a sidebar (on the left) and a main area. The sidebar is displayed with a distinct background color (light blue) and typically contains input controls (currently only a slider input). The main area occupies by default 2/3 of the horizontal width and typically contains outputs (currently our histogram).The help page for this latter function is here. Have a look at it. From it, you can also access the sidebarPanel()
and mainPanel()
help pages.
In our case, the sidePanel contains only one UI element: a sliderInput
. The mainPanel also contains one input element: a plotOutput
. Look at the description of these functions here.
Note: You can run a Shiny app by giving the name of its directory to the function runApp. For example if your Shiny app is in a directory called my_app, run it with the following code:
library(shiny)
runApp("my_app")
If you would like your app to display in showcase mode, you can run runApp("my_app", display.mode = "showcase")
.
You can use one of Shiny’s HTML tag functions. These functions parallel common HTML5 tags. Let’s try out a few of them.
shiny function | HTML5 equivalent | creates |
---|---|---|
p() |
<p> |
A paragraph of text |
h1() |
<h1> |
A first level header |
h2() |
<h2> |
A second level header |
h3() |
<h3> |
A third level header |
h4() |
<h4> |
A fourth level header |
h5() |
<h5> |
A fifth level header |
h6() |
<h6> |
A sixth level header |
a() |
<a> |
A hyper link |
br() |
<br> |
A line break (e.g. a blank line) |
div() |
<div> |
A division of text with a uniform style |
span() |
<span> |
An in-line division of text with a uniform style |
pre() |
<pre> |
Text ‘as is’ in a fixed width font |
code() |
<code> |
A formatted block of code |
img() |
<img> |
An image |
strong() |
<strong> |
Bold text |
em() |
<em> |
Italicized text |
HTML() |
Directly passes a character string as HTML code |
If only the ui.R
file was present, no dynamic content would be available. This is the purpose of the server.R
file to add interactivity.
At this point, we have to explain how the ui.R
and server.R
files communicate.
In the server.R
file, we see a rendering function called renderPlot()
, as well as two interesting pieces of code: output$distPlot
and input$bins
. Note that distPlot
is the ID (outputId) used when we called the plotOutput()
function, while bins
is the ID (inputId) used when we called the sliderInput()
function. Consult the help pages for these two functions.
It is through these ID that Shiny couples the UI elements defined in ui.R
to the dynamic content in server.R
.
The input
variable of the shinyServer()
functions contains all the UI elements, the output
variable returns the dynamic content to the UI.
Exercise 1: Modify the ui.R
and server.R
files so as to add a textInput
in the sidebarPanel (below the sliderInput) that will enable you to modify the title of the histogram. The histogram should now be created using the functions ggplot()
, geom_histogram()
and ggtitle()
from the ggplot2
R package.
The location of code in server.R
determines how often it is run, and thus impacts the performance of the app. There are three zones where code is run more or less often:
# Zone 1
library(shiny)
library(ggplot2)
shinyServer(function(input, output) {
# Zone 2
output$distPlot <- renderPlot({
# Zone 3 : run each time a user changes a widget that output$map relies on
})
})
Examples/exercises below will help to clarify these notions.
Normally, Shiny automatically reruns the code in the different zones as explained above. But consider the following fictitious example where two lines were added in Zone 3:
# Zone 1
library(shiny)
library(ggplot2)
shinyServer(function(input, output) {
# Zone 2
output$distPlot <- renderPlot({
# Zone 3
dat <- get_data(input$ui_element1, input$ui_element2)
plot(dat, input$ui_element3)
})
})
The (hypothetical, user-defined) get_data()
function is called each time any of the three UI elements is updated (since it is in Zone 3). However, if only ui_element3
is updated the data should not be reloaded (to prevent time loss). This is where reactive expressions come in, as seen below:
# Zone 1
library(ggplot2)
shinyServer(function(input, output) {
# Zone 2
input_data <- reactive({
dat <- get_data(input$ui_element1, input$ui_element2)
})
output$distPlot <- renderPlot({
# Zone 3
plot(input_data(), input$ui_element3)
})
})
We wrap the data loading in a reactive block, and in the plotting refer to the new function input_data()
. The advantage of this approach is that for a reactive expression Shiny first checks if the data actually needs to be updated or not. If only ui_element3
is updated, the reactive simply returns the previously stored data (i.e., caching), and the plot is redrawn without the costly step of re-reading the data.
At this point, you might want to have a better look at the Shiny cheatsheet that you downloaded previously.
Exercise 2. Open a new Shiny app in RStudio (File > New project > New directory > Shiny Web Application), which is essentially a new project in itself. Then edit the ui.R
and server.R
files to obtain the example given below (the actual plot is of mtcars$mpg
as a function of mtcars$wt
). Use the ggplot2
package to make the plot. For this exercise, no interactivity is required. In the next couple of excercises you will build a visualisation app for the mtcars
dataset.
Exercise 3. Add another dropdown menu in the sidebar. The two dropdown menus should now select which variables from mtcars
will be respectively plotted on the \(x\) and \(y\) axes. Tip: have a look at aes_string()
from ggplot2
. Also, the textInput
in the sidebarPanel should now be connected interactively to the actual title of the plot. And the names of the two variables selected should now be displayed above the plot in an interactive manner (“You selected variables … and …”).
Exercise 4. Add a checkbox which enables the user to choose whether or not to include a stat_smooth()
call (from ggplot2
package).
Exercise 5. Add a checkbox and a dropdown menu that allow the user to toggle the use of facetting and select which variable to facet with. Tip: see for instance this web page.
In the next couple of excercises we will build a stock price visualisation app (inspired by Shiny tutorial).
Exercise 6. We will download the stock price data using getSymbols()
from the quantmod
package. Ensure that the value of the argument auto.assign
is set to FALSE
, and you can use the from
and to
input arguments to select time. Extract stockprice data for AAPL (Apple), YHOO (Yahoo), and GS (Goldman Sachs). Create a chart of that data by calling the candleChart()
function from quantmod
on the resulting object from getSymbols()
. Change the theme from black to white.
Exercise 7. Create a new Shiny app, and create a ui.R
file that results in this user interface: