Chapter 4 Adding visuals
This final chapter of learning R Shiny will go through how to add charts and tables to your app, including interactivity (e.g. plotly
).
4.1 Adding charts to Shiny
4.1.1 A brief recap on chart outputs
For ggplot charts, use the renderPlot({})
function in the server, containing code to create the chart. This is assigned to a named object in the output list.
Uses the plotOutput()
function in the UI, with the output name passed as the first argument.
Example in the UI:
Example in the server:
4.1.2 Adding a ggplot chart: Server
As mentioned before, you usually create your charts and table outputs in the Server.
To add a chart to your server, it is recommended to create your ggplot chart outside of your Shiny app to check that it works properly, as it is very difficult to debug in an interactive environment.
In the server, create a new renderPlot({})
call, and assign it to a new, unique output object i.e. output$name
. Do not forget the round and curly brackets.
Add your ggplot code inside the renderPlot({})
call, ensuring that the final object referenced in your ggplot you want to display.
4.1.3 Adding a ggplot chart: UI
Once you’ve created your chart in the server, it’s time to display it in the UI. Remember that the position of the code in the UI file sets the position of the output in the final UI. Add a plotOutput()
call, with the first argument being the name
you gave your plot output in the server. Finally, run your app to check it worked!
Points to note:
- you can’t use the same name for multiple objects, even if you want to show the same object twice!
- code contained in the
renderPlot({})
call can be as simple or complex as you like - objects created inside the
renderPlot({})
call can’t be referenced outside of it; you can only call the whole output object
4.1.4 Adding input reactivity
Adding user interactivity to your charts can be done using your inputs. Input values can be used exactly like any other named object in a list, called using input$name
inside a reactive or render call. Shiny refreshes outputs automatically every time a linked input is updated.
Static code (always return 2021 data)
Dynamic code (returns a year the user selects)
4.1.5 Exercise
15:00
Open Chapter 4 > Exercise-4.1.5
in the training repository and complete the following tasks:
- Modify the year input to allow you to filter on every single
Eurovision
year. - Make sure your
Eurovision
data is filtered in a reactive to the year(s) selected by the user. - Create a new
renderPlot
call and create a ggplot chart of the filteredEurovision
song contest data to it. - Add a
plotOutput
call in the UI, and check that this works in your app.
Bonus
- Try adding another input filter to your app (e.g. filter on country). Can you use this to filter your chart on two inputs at once?
4.1.5 Solution
UI
library(shiny)
library(lubridate)
library(gapminder)
library(dplyr)
library(ggplot2)
eurovision_data <-
read.csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-17/eurovision.csv")
shinyUI(fluidPage(
titlePanel("My Shiny App"),
sidebarLayout(
sidebarPanel(
selectInput("year",
label = "Select Year(s):",
choices = c(1956:2022),
### Question 1: Check the year input allows users to filter
### on every single `Eurovision` year
## START HERE ----
selected = c(1956:2022),
multiple = TRUE
),
### Question 5 (Bonus): Try adding another input to allow users to select on artist country
## Hint:
# - You need to read in the Eurovision data outside the shiny app to allow
# the choices for country input widget to work
## START HERE ----
selectInput("country",
label = "Select Artist Country",
choices = unique(eurovision_data$artist_country)
),
downloadButton("downloadData", "Download the data")
),
mainPanel(
dataTableOutput("eurovisionTable"),
### Question 4: Add a plotOutput call in the UI
## START HERE ----
plotOutput("eurovisionPlot")
)
)
))
Server
library(shiny)
library(lubridate)
library(gapminder)
library(dplyr)
library(ggplot2)
shinyServer(function(input, output) {
eurovision_filtered <- reactive({
eurovision_data <-
read.csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-17/eurovision.csv") %>%
### Question 2: Make sure your `Eurovision` data is filtered in a reactive to the year(s) selected by the user
## START HERE ----
dplyr::filter(year %in% input$year) %>%
### Question 5 (Bonus): Try adding another input filter to allow users to select on country
## START HERE ----
dplyr::filter(artist_country %in% input$country)
})
output$eurovisionTable <- renderDataTable({
eurovision_filtered()
})
output$downloadData <- downloadHandler(
filename = function() {
paste("Eurovision_data-", lubridate::today(), ".csv", sep = "")
},
content = function(con) {
write.csv(eurovision_filtered(), con)
}
)
### Question 3: Create a new renderPlot call and create a ggplot chart of the
### filtered data
## START HERE ----
output$eurovisionPlot <- renderPlot({
ggplot(eurovision_filtered(),
aes(x = year, y = total_points, color = artist_country)) +
geom_line() +
labs(title = "Eurovision Points Over Time", x = "Year", y = "Points")
})
})
Training repository: Go to Chapter 4
> Exercise-4.1.5
> Solution
Run either the ui.R
or server.R
file to see the app in action.
4.2 Dynamic charts with plotly
4.2.1 The plotly package
The plotly
R package is a way to create charts in R with a high level of innate interactivity. This includes things like selecting and deselecting individual series, mouse-over capabilities to see values, and zooming. These visualisations have more functionality, and are also more attractive than standard ggplot charts. You can see some of the possibilities here.
4.2.2 ggplotly
You can create charts in native Plotly
code; this gives you lots of flexibility but is very different to ggplot so is quite a steep learning curve! Luckily, there is a simple alternative, the ggplotly()
function
Wrapping any ggplot code in ggplotly()
converts the code to plotly
code, and can be used in exactly the same way as native plotly
code. This is done inside the render call, and is done on the complete ggplot object.
4.2.3 Using the right functions
When using plotly
charts in Shiny, it is really important to use the correct render and output calls. Unlike ggplot charts, plotly
doesn’t work with the native Shiny renderPlot
and plotOutput
functions
You need to make sure you use the plotlyOutput
and renderPlotly
calls in the UI and server respectively. These plotly
specific functions also won’t work with ggplot charts, so it’s important to use the right functions in each case.
4.2.4 Exercise
15:00
Open Chapter 4 > Exercise-4.2.4
in the training repository and complete the following tasks:
- Use the
ggplotly
call to turn your ggplot into aplotly
chart. Hint: Make sure you call theplotly
library! - Add your chart code to a
renderPlotly()
call in the server. Don’t forget:
- It won’t work in a
renderPlot()
call - It needs a unique name
- Add your interactive chart to the UI using the
plotlyOutput()
call
4.2.4 Solution
UI
library(shiny)
library(lubridate)
library(gapminder)
library(dplyr)
library(ggplot2)
library(plotly)
eurovision_data <-
read.csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-17/eurovision.csv")
shinyUI(fluidPage(
titlePanel("My Shiny App"),
sidebarLayout(
sidebarPanel(
selectInput("year",
label = "Select Year(s):",
choices = unique(eurovision_data$year),
selected = unique(eurovision_data$year),
multiple = TRUE
),
selectInput("country",
label = "Select Artist Country",
choices = unique(eurovision_data$artist_country)
),
downloadButton("downloadData", "Download the data")
),
mainPanel(
dataTableOutput("eurovisionTable"),
### Question 3: Add your interactive chart to the UI using the plotlyOutput()
## - Optional: you can change the input `name` to something more appropriate
## However, remember this needs to be same as the output `name` in the
## renderPlotly({}) in the server
## START HERE ----
plotlyOutput("eurovisionPlotly")
)
)
))
Server
library(shiny)
library(lubridate)
library(gapminder)
library(dplyr)
library(ggplot2)
library(plotly)
shinyServer(function(input, output) {
eurovision_filtered <- reactive({
eurovision_data <-
read.csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-17/eurovision.csv") %>%
dplyr::filter(year %in% input$year) %>%
dplyr::filter(artist_country %in% input$country)
})
output$eurovisionTable <- renderDataTable({
eurovision_filtered()
})
output$downloadData <- downloadHandler(
filename = function() {
paste("Eurovision_data-", lubridate::today(), ".csv", sep = "")
},
content = function(con) {
write.csv(eurovision_filtered(), con)
}
)
### Question 1 & 2: Use ggplotly() to turn your ggplot into a plotly chart.
## Add your chart code to a renderPlotly call in the server
# Hint:
## - Remember it's going to be inside a renderPlotly({}) function
## - Optional: you can change the output `name` to something more appropriate
## However, remember this needs to be same as the input `name` in the plotlyOutput()
## in the UI
## START HERE ----
output$eurovisionPlotly <- renderPlotly({
ggplotly(
ggplot(eurovision_filtered(),
aes(x = year, y = total_points, color = artist_country)) +
geom_line() +
labs(title = "Eurovision Points Over Time", x = "Year", y = "Points")
)
})
})
Training repository: Go to Chapter 4
> Exercise-4.2.4
> Solution
Run either the ui.R
or server.R
file to see the app in action.
4.3 Data tables
4.3.1 A brief recap of table outputs
The renderDataTable()
and dataTableOutput()
functions you have previously used come from the DT
package. These tables have a high level of inbuilt functionality, so users can search and order the table without you needing to change anything.
Example in the UI:
The dataTableOutput()
function is used in the UI, with the output name passed as the first argument.
Example in the server:
The renderDataTable({})
function is used in the server, containing code to create the text itself. This is assigned to a named object in the output list.
4.3.2 Data table formatting
You can change a number of arguments inside the DT::datatable
function to improve the appearance or usability of the table, including:
* pass a vector of clean column names to the table using the colnames
argument
* add a custom caption for the table using the caption argument
* allow users to edit values in the table using editable = “cell”
4.3.3 Data table extensions
You can also add even more functionality to dataTables using DT add-ons. You can enable these in the dataTable
function by calling the extensions argument. You can see examples of all the available extensions here, but the most frequently used one is Buttons. Buttons adds download buttons to the top of your data table to allow users to download the data in a range of formats:
4.3.4 Exercise
15:00
Open Chapter 4 > Exercise-4.3.4
in the training repository and complete the following tasks:
- Call
library(DT)
in your server file. - You should already have a DT data table which uses your existing
Eurovision
reactive as the source data. Make sure you can filter on year! - Add a caption to your table explaining what the data is.
- Add buttons to the data table to allow downloading the data as a CSV or XLSX file.
Bonus
- Add a button that allows the user to print the data straight from the data table.
- Make the scores editable so users can choose their own personal winner of
Eurovision
.
4.3.4 Solution
UI
library(shiny)
library(lubridate)
library(gapminder)
library(dplyr)
library(ggplot2)
library(plotly)
library(DT)
eurovision_data <-
read.csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-17/eurovision.csv")
shinyUI(fluidPage(
titlePanel("My Shiny App"),
sidebarLayout(
sidebarPanel(
selectInput("year",
label = "Select Year(s):",
choices = unique(eurovision_data$year),
selected = unique(eurovision_data$year),
multiple = TRUE
),
selectInput("country",
label = "Select Artist Country",
choices = unique(eurovision_data$artist_country)
)
),
mainPanel(
### Question 2: Use the DT::dataTableOutput function
## START HERE ----
DT::dataTableOutput("eurovisionTable")
)
)
))
Server
library(shiny)
library(lubridate)
library(gapminder)
library(dplyr)
library(ggplot2)
library(plotly)
library(DT)
shinyServer(function(input, output) {
eurovision_filtered <- reactive({
eurovision_data <-
read.csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-17/eurovision.csv") %>%
dplyr::filter(year %in% input$year) %>%
dplyr::filter(artist_country %in% input$country)
})
## Question 2: Use the DT::renderDataTable function
## START HERE ----
output$eurovisionTable <- DT::renderDataTable({
### Question 3: Add a caption to the data to explain what it is.
### Question 4: Add buttons to the data to allow users to download the data as
### a csv or xlsx file
### Bonus ###
### Question 5: Add a button to the data to allow users to print the data
## straight from the data table
### Question 6: Make the scores editable so users can choose their own
### person winner of Eurovision
## START HERE ----
DT::datatable(
eurovision_filtered(),
editable = "cell",
extensions = "Buttons",
options = list(
caption = "This table shows the Eurovision data from 1956 to 2022.",
dom = "Bfrtip",
buttons = c('print', 'csv', 'excel')
)
)
})
})
Training repository: Go to Chapter 4
> Exercise-4.3.4
> Solution
Run either the ui.R
or server.R
file to see the app in action.
You have reached the end of the learning content. Proceed to Chapter 5 to explore additional resources that can help you further solidify your knowledge and skills in Shiny.