S3729C Wellness and Health Mangement
Lesson 09 Conducted on 18 August 2024
For your assignment, you need to develop a dashboard in the form of
an R Shiny Web Application, which will comprise a display to encompass
data visualization of form fills, data modelling and comparison against
norms, and a qualitative analysis segment incorporating text analytics
and sentiment analysis.

This lesson will help to ensure you have the necessary competencies to develop the desired output for your CWF assignment.
At the end of this unit comprising Lessons 09 to 11, you would be able to:
Please follow the steps outlined below to set up your R Studio Cloud Account, which will be used for this session.




Congratulations, your R Studio Cloud Account is now ready !

The R Studio IDE Workspace consists of the following key components



This is the code for installation of Pacman which is used to unpack all packages required in this lesson.
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.1'
## (as 'lib' is unspecified)
We will then proceed to load the packages required for this section
pacman::p_load(pacman, psych, rio, tidyverse, ggplot2, ggridges, devtools, vioplot, dplyr, ggwordcloud, ggforce, gridExtra, kableExtra, shiny)For this session, we will be using the movies dataset, which combines data from two websites: the Internet Movie Database, commonly known as IMDB, and Rotten Tomatoes. The observations are a random sample of 651 movies released in the US between 1970 and 2014.
GitHub Link for Movies Dataset (in csv) : https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")Check on the output and hence the integrity of the loaded data by reading the column names. You can also see that the data consists of the following fields
## [1] "X" "title" "title_type" "genre"
## [5] "runtime" "mpaa_rating" "studio" "thtr_rel_date"
## [9] "thtr_rel_year" "thtr_rel_month" "thtr_rel_day" "dvd_rel_date"
## [13] "dvd_rel_year" "dvd_rel_month" "dvd_rel_day" "imdb_rating"
## [17] "imdb_num_votes" "critics_rating" "critics_score" "audience_rating"
## [21] "audience_score" "best_pic_nom" "best_pic_win" "best_actor_win"
## [25] "best_actress_win" "best_dir_win" "top200_box" "director"
## [29] "actor1" "actor2" "actor3" "actor4"
## [33] "actor5" "imdb_url" "rt_url"
You can also use the following command to view a sample of 10 movies with all 35 fields in a table
| X | title | title_type | genre | runtime | mpaa_rating | studio | thtr_rel_date | thtr_rel_year | thtr_rel_month | thtr_rel_day | dvd_rel_date | dvd_rel_year | dvd_rel_month | dvd_rel_day | imdb_rating | imdb_num_votes | critics_rating | critics_score | audience_rating | audience_score | best_pic_nom | best_pic_win | best_actor_win | best_actress_win | best_dir_win | top200_box | director | actor1 | actor2 | actor3 | actor4 | actor5 | imdb_url | rt_url | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 180 | 180 | Resident Evil | Feature Film | Action & Adventure | 100 | R | Columbia Tristar Pictures | 2002-03-15 05:00:00 | 2002 | 3 | 15 | 2002-07-30 04:00:00 | 2002 | 7 | 30 | 6.7 | 192052 | Rotten | 33 | Upright | 68 | no | no | no | no | no | no | Paul W.S. Anderson | Milla Jovovich | Michelle Rodriguez | Eric Mabius | James Purefoy | Martin Crewes | http://www.imdb.com/title/tt0120804/ | //www.rottentomatoes.com/m/resident_evil/ |
| 181 | 181 | Top Gun | Feature Film | Drama | 110 | PG | Paramount Pictures | 1986-05-16 04:00:00 | 1986 | 5 | 16 | 1998-10-20 04:00:00 | 1998 | 10 | 20 | 6.9 | 211129 | Rotten | 54 | Upright | 83 | no | no | no | no | no | yes | Tony Scott | Tom Cruise | Kelly McGillis | Anthony Edwards | Val Kilmer | Tom Skerritt | http://www.imdb.com/title/tt0092099/ | //www.rottentomatoes.com/m/top_gun/ |
| 182 | 182 | 25th Hour | Feature Film | Drama | 135 | R | Touchstone Pictures | 2003-01-10 05:00:00 | 2003 | 1 | 10 | 2003-05-20 04:00:00 | 2003 | 5 | 20 | 7.7 | 146518 | Certified Fresh | 78 | Upright | 85 | no | no | yes | no | no | no | Spike Lee | Edward Norton | Barry Pepper | Philip Seymour Hoffman | Rosario Dawson | Anna Paquin | http://www.imdb.com/title/tt0307901/ | //www.rottentomatoes.com/m/25th_hour/ |
| 183 | 183 | The Bad News Bears in Breaking Training | Feature Film | Comedy | 100 | PG | Paramount Pictures | 1977-07-08 04:00:00 | 1977 | 7 | 8 | 2002-02-12 05:00:00 | 2002 | 2 | 12 | 5.4 | 2239 | Rotten | 50 | Spilled | 39 | no | no | no | no | no | no | Michael Pressman | William Devane | Jackie Earle Haley | Clifton James | Jimmy Baio | Chris Barnes | http://www.imdb.com/title/tt0075718/ | //www.rottentomatoes.com/m/bad_news_bears_in_breaking_training/ |
| 184 | 184 | Streets of Gold | Feature Film | Drama | 95 | R | Live Home Video | 1986-11-14 05:00:00 | 1986 | 11 | 14 | NA | NA | NA | NA | 6.0 | 486 | Rotten | 31 | Spilled | 44 | no | no | no | no | no | no | Joe Roth | Klaus Maria Brandauer | Adrian Pasdar | Richard Pasdar | Wesley Snipes | Angela Molina | http://www.imdb.com/title/tt0092022/ | //www.rottentomatoes.com/m/streets_of_gold/ |
| 185 | 185 | The Mod Squad | Feature Film | Drama | 92 | R | MGM | 1999-03-26 05:00:00 | 1999 | 3 | 26 | 1999-08-24 04:00:00 | 1999 | 8 | 24 | 4.1 | 7658 | Rotten | 4 | Spilled | 17 | no | no | no | no | no | no | Scott Silver | Claire Danes | Giovanni Ribisi | Omar Epps | Josh Brolin | Dennis Farina | http://www.imdb.com/title/tt0120757/ | //www.rottentomatoes.com/m/mod_squad/ |
| 186 | 186 | Where the Heart Is | Feature Film | Drama | 107 | R | Touchstone Pictures | 1990-01-01 05:00:00 | 1990 | 1 | 1 | 2003-06-03 04:00:00 | 2003 | 6 | 3 | 6.1 | 1268 | Rotten | 11 | Spilled | 58 | no | no | no | no | no | no | John Boorman | Dabney Coleman | Uma Thurman | Joanna Cassidy | Crispin Glover | Suzy Amis | http://www.imdb.com/title/tt0100924/ | //www.rottentomatoes.com/m/1028561-where_the_heart_is/ |
| 187 | 187 | Battlefield Earth | Feature Film | Action & Adventure | 118 | PG-13 | Warner Bros. Pictures | 2000-05-12 04:00:00 | 2000 | 5 | 12 | 2001-01-16 05:00:00 | 2001 | 1 | 16 | 2.4 | 64119 | Rotten | 3 | Spilled | 11 | no | no | yes | no | no | no | Roger Christian | John Travolta | Barry Pepper | Forest Whitaker | Kim Coates | Richard Tyson | http://www.imdb.com/title/tt0185183/ | //www.rottentomatoes.com/m/battlefield_earth/ |
| 188 | 188 | Osmosis Jones | Feature Film | Comedy | 95 | PG | Warner Bros. Pictures | 2001-08-10 04:00:00 | 2001 | 8 | 10 | 2001-11-13 05:00:00 | 2001 | 11 | 13 | 6.2 | 24678 | Rotten | 55 | Spilled | 41 | no | no | no | no | no | no | Bill Murray | Bill Murray | Chris Rock | Laurence Fishburne | David Hyde Pierce | William Shatner | http://www.imdb.com/title/tt0181739/ | //www.rottentomatoes.com/m/osmosis_jones/ |
| 189 | 189 | Never Cry Wolf | Feature Film | Drama | 105 | PG | Disney | 1983-01-01 05:00:00 | 1983 | 1 | 1 | 2000-02-22 05:00:00 | 2000 | 2 | 22 | 7.6 | 6114 | Fresh | 100 | Upright | 85 | no | no | no | no | no | no | Carroll Ballard | Charles Martin Smith | Brian Dennehy | Zachary Ittimangnaq | Samson Jorah | Hugh Webster | http://www.imdb.com/title/tt0086005/ | //www.rottentomatoes.com/m/never_cry_wolf/ |
| 190 | 190 | A Good Woman | Feature Film | Drama | 93 | PG | Lions Gate Films | 2006-02-03 05:00:00 | 2006 | 2 | 3 | 2006-06-13 04:00:00 | 2006 | 6 | 13 | 6.5 | 9399 | Rotten | 37 | Spilled | 49 | no | no | no | yes | no | no | Mike Barker | Helen Hunt | Scarlett Johansson | Tom Wilkinson | Stephen Campbell Moore | Mark Umbers | http://www.imdb.com/title/tt0379306/ | //www.rottentomatoes.com/m/good_woman/ |
Shiny is an R package that makes it easy to build interactive web apps straight from R. It combines the computational power of R with the interactivity of the modern web.
Here is an example of a Shiny App that you would build by the end of this session, allowing end users to navigate the dataset on movies that you have just loaded.

This will contain the following :
Every Shiny app has a webpage that the user visits, and behind this webpage there is a computer that serves this webpage by running R.
When running your app locally, the computer serving your app is your computer.
When your app is deployed, the computer serving your app is a web server.
Each app is comprised of two components, a UI and a server.
The UI is ultimately built with HTML, CSS, and JavaScript. However, you as the Shiny developer do not need to know these languages. Shiny lets R users write user interfaces using a simple, familiar-looking API. However there are no limits to customization for advanced users who are familiar with these languages.
The server function contains the instructions to map user inputs to outputs.
Lets start by taking a look at the anatomy of a Shiny App.
Here is the code for the development of a simple app. You can copy it into your R Studio IDE workspace, select all of the code, and press CTRL + Enter on your keypad to preview the output.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
# Inputs: Select variables to plot
sidebarPanel(
# Select variable for y-axis
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "imdb_rating"
),
# Select variable for x-axis
selectInput(
inputId = "x",
label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "critics_score"
)
),
# Output: Show scatterplot
mainPanel(
plotOutput(outputId = "scatterplot")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y)) +
geom_point()
})
}
# Create a Shiny app object ----------------------------------------------------
shinyApp(ui = ui, server = server)We will now go into the User Interface (UI) and Server Function as they are applied in the development of this app.
In this section we’ll build the user interface of a simple app.
However, before we get into the weeds of building a user interface, let’s revisit the anatomy of a Shiny app.
For example, if your app features a plot, the code for building that plot lies in the server function. However, the setup for the user defined inputs for the plot, as well as information on where physically on the app the plot should appear, are defined in the UI.
Here is the app we’ll work with in this section and the code that builds the UI of that app.
Since this is too much code to parse, we’ll explore individual components of the UI one by one.
At the outermost layer of our UI definition we begin with the fluidPage() function.
The fluidPage() function creates a fluid page layout consisting of rows and columns. Rows make sure that elements in them appear on the same line. Columns within these rows define how much horizontal space each element should occupy.
Fluid pages scale their components in realtime to fill all available browser width, which means you, the app developer, don’t need to worry about defining relative widths for individual app components.
As always, for more information on arguments to this function, you
can view the R function help by typing ?f luidPage in your R console or
visiting the function reference page on the package website
here
Next, we define the layout of our app with sidebarLayout().
Shiny includes a number of options for laying out the components of an application. The default layout, the one we’re using in our example app, is a layout with a sidebar, that you can define with the sidebarLayout() function.
This is a simple layout with a narrow sidebar for inputs and a wider main area for output.
Under the hood, Shiny implements layout features available in Bootstrap 2, which is a popular HTML/CSS framework. However the nice thing about working in Shiny is that no prior experience with Bootstrap is necessary.
To learn more about various layouts, I recommend reviewing the
Application
Layout Guide article at https://shiny.rstudio.com/.
Next we define our sidebar panel containing input controls.
This panel contains two dropdown menus created with the selectInput() function.
Let’s take a look at one of the selectInput widgets a little more closely.
The final component of our UI is mainPanel().
In this example provided, the main panel contains only one component, a plot output. We’ll talk about how this plot is built later on in this lesson.
The key purpose of the server function is to define the relationship between inputs and outputs.
In the earlier section, we showed you how to build the UI of this app, and also noted that each input was tagged with an inputId that can be used to refer to them in the server.
Once again there is a lot going on here to parse at once, so in the following sections we take a closer look at the function.
We define our server function which takes two arguments: an input and an output. Both of these are named lists.
The server function accesses inputs selected by the user to perform computations and specifies how outputs laid out in the UI should be updated.
Our simple app had only one output – a plot. So our server function contains the logic necessary to build this plot.
The renderPlot() function specifies how the plot output should be updated. Let’s take a look at what is happening in the renderPlot() function first.
You may have noticed that this is none other than ggplot2 code which we covered in lesson 11.
One aspect of the syntax that might be new, however, is how the x and y variables are defined. They come from the input list that is built in the UI.
Here is the relevant UI and server code.
Input x and y come from the selectInput() widgets, and map to the x and y arguments of the plot aesthetics.
There are three rules of building server functions:
Always save objects to display to the named output list, i.e. something of the form output$xx, where xx is the plot you want to display.
Always build objects to display with one of the render*() functions, like we built our plot with renderPlot().
Use input values from the named input list, with input$xx.
Just like various inputs, Shiny also provides a wide selection of output types each of which works with a render function.
For example, in our app we used the renderPlot() function to build our reactive plot (we’ll get to what I mean by reactive in a second) and laid out the plot with the plotOutput() function.
Shiny knows to match these two together as they use the same outputID, scatterplot.
Let’s also briefly discuss reactivity, which we will go into further details in a subsequent section.
It’s easy to build interactive applications with Shiny, but to get the most out of it, you’ll need to understand the reactive programming scheme used by Shiny.
In a nutshell Shiny automatically updates outputs, such as plots, when inputs that go into them change.
The last component of a Shiny app is aptly named shinyApp() function, which puts the UI and the server pieces together to create a Shiny app object.
Let us now take a look at a further development of the R Shiny App Scatterplot derived from the movies data, which now adds a third dimension to the variables, that being color (spelt the American way because R is coded this way)

Similar to what you did earlier, copy the code into your R Studio IDE, highlight and press CTRL + Enter to preview the output.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
# Inputs: Select variables to plot
sidebarPanel(
# Select variable for y-axis
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics score" = "critics_score",
"Audience score" = "audience_score",
"Runtime" = "runtime"
),
selected = "audience_score"
),
# Select variable for x-axis
selectInput(
inputId = "x",
label = "X-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics score" = "critics_score",
"Audience score" = "audience_score",
"Runtime" = "runtime"
),
selected = "critics_score"
),
# Select variable for color
selectInput(
inputId = "z",
label = "Color by:",
choices = c(
"Title type" = "title_type",
"Genre" = "genre",
"MPAA rating" = "mpaa_rating",
"Critics rating" = "critics_rating",
"Audience rating" = "audience_rating"
),
selected = "mpaa_rating"
)
),
# Output: Show scatterplot
mainPanel(
plotOutput(outputId = "scatterplot")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(
x = input$x, y = input$y,
color = input$z
)) +
geom_point()
})
}
# Create a Shiny app object ----------------------------------------------------
shinyApp(ui = ui, server = server)The concept of reactivity is key to Shiny App development.
Suppose you have a sliderInput in your app with the inputId of alpha. The value of this input is stored in input$alpha.

So when the user moves around the slider, the value of the alpha input is updated in the input list.
Reactivity automatically occurs when an input value is used to render an output object, i.e. in the server function below the plot is re-rendered when the value of input alpha changes based on user input. You, as the app developer, do not need to write code that says “Update the plot every time the value of input alpha changes”, Shiny automatically takes care of this for you in the render*() function.
Here is a roadmap of the reactive flow in Shiny.

The user selects an input, this input goes through some expression in the server, and an output is rendered. Each time the user changes their input selection, the expression that generates the output will automatically re-execute, and the relevant output will be re-rendered based on the new value of the input.
In a Shiny application, there’s no need to explictly describe the relationships between inputs and outputs and tell R what to do when each input changes, Shiny automatically handles these details for you.
The following is code for a new input widget, a sliderInput, that controls the transparency of the plotted points.

This widget should have the ID alpha and its values should range between 0 and 1. You can decide what the displayed label and initial value of the slider should be.
Similar to before, copy the code into your R Studio IDE, highlight and press CTRL + Enter to preview the output.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "audience_score"
),
selectInput(
inputId = "x",
label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "critics_score"
),
sliderInput(
inputId = "alpha",
label = "Alpha:",
min = 0, max = 1,
value = 0.5
)
),
mainPanel(
plotOutput(outputId = "scatterplot")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y)) +
geom_point(alpha = input$alpha)
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)The goal of this section is to build familiarity with a few UI input functions.
Shiny provides a wide selection of input widgets. The Shiny cheatsheet is a great place to see a list of them all at once.
Once you know which one you want to use, you can find out more about it in the package documentation.

Let’s start with a checkboxInput().
Suppose we want to add checkbox input to our app to specify whether the data plotted should be shown in a data table. We need to make three modifications to our app to accomplish this.
A cautionary tale before we move on – watch for your commas! Remember that this widget definition goes in the sidebarPanel(). In this panel we separate out widget with commas.
For example:
mainPanel(
# Show scatterplot
plotOutput(outputId = "scatterplot"),
# Show data table
dataTableOutput(outputId = "movestable")
)## `shiny::dataTableOutput()` is deprecated as of shiny 1.8.1.
## Please use `DT::DTOutput()` instead.
## Since you have a suitable version of DT (>= v0.32.1), shiny::dataTableOutput() will automatically use DT::DTOutput() under-the-hood.
## If this happens to break your app, set `options(shiny.legacy.datatable = TRUE)` to get the legacy datatable implementation (or `FALSE` to squelch this message).
## See <https://rstudio.github.io/DT/shiny.html> for more information.
The second step was to add an output to the UI defining where the data table should appear. Note that for this we’re using the dataTableOutput() function.
This function takes one argument, the outputId. Again, you can define to be anything we want, but short and informative names are the best.
# Print data table if checked
output$moviestable <- renderDataTable({
if(input$show_data){
DT::datatable(data = movies %>% select(1:7),
options = list(pageLength = 10),
rownames = FALSE)
}
})Lastly, in our server, we describe how this table should be calculated. We use the renderDataTable() function to build this table.
Note that the first line of code in the function is an if statement, telling the app to only do this if input$show_data is TRUE. We also specify some other arguments to datatable(), mostly for cosmetic reasons.
Here is the resulting app, with the box checked:
and box unchecked:
Here is the code. You may copy the code into your R Studio IDE, highlight and press CTRL + Enter to preview the output.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(dplyr)
library(DT)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
# Select variable for y-axis
selectInput(inputId = "y", label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "audience_score"),
# Select variable for x-axis
selectInput(inputId = "x", label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score",
"audience_score", "runtime"),
selected = "critics_score"),
# Show data table
checkboxInput(inputId = "show_data",
label = "Show data table", value = TRUE)
),
mainPanel(
# Show scatterplot
plotOutput(outputId = "scatterplot"),
# Show data table
dataTableOutput(outputId = "moviestable")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$moviestable <- renderDataTable({
if(input$show_data){
DT::datatable(data = movies %>% select(1:7),
options = list(pageLength = 10),
rownames = FALSE)
}
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)The goal of this section is to build familiarity with a few rendering functions.
Shiny provides a wide selection of output types, each of which works with a render function. We looked at the renderPlot() function before.
Now let’s take a look at the renderTable() function.
This is the app you’ve seen numerous times so far that we will use as our starting point.
And this is the final app that we want to achieve, with a summary table beneath the plot.
So we want to add a summary table beneath the plot displaying summary statistics for a new variable we’ll create: score_ratio, the ratio of audience scores to critics scores.
We need to make four modifications to our app to accomplish this.
# Create new variable:
# ratio of critics and audience scores
movies <- movies %>%
mutate(score_ratio = audience_score / critics_score)First is creating the new variable, which we can do outside of the ui and the server so it’s calculated once when our app launches.
There are many ways one can create the new variable in R. We do so here using the mutate() function from the dplyr package.
# Subset for title types
checkboxGroupInput(inputId = "selected_title_type",
label = "Select title type:",
choices = levels(moves$title_type),
selected = levels(movies$title_type))mainPanel(
# Show scatterplot
plotOutput(outputId = "scatterplot"),
# Show data table
tableOutput(outputId = "summarytable")
)We use the tableOutput() function for this. This function takes one argument, the outputId.
Lastly, in the server, we describe how this table should be calculated with the renderTable() function.
output$summarytable <- renderTable(
{
movies %>%
filter(title_type %in% input$selected_title_type) %>%
group_by(mpaa_rating) %>%
summarise(mean_score_ratio = mean(score_ratio), SD = sd(score_ratio), n = n())
},
striped = TRUE,
spacing = "l",
align = "lccr",
digits = 4,
width = "90%",
caption = "Score ratio (audience / critics' scores) summary statistics by MPAA rating."
)Note that the name of the output created by the render function should match the name we used for the output in the ui (summarytable).
The first argument is the expression that returns an R object in tabular form.
{
movies %>%
filter(title_type %in% input$selected_title_type) %>%
group_by(mpaa_rating) %>%
summarise(mean_score_ratio = mean(score_ratio), SD = sd(score_ratio), n = n())
}Note that we wrap the expression with curly braces. The expression first filters for the selected title types. Since this is a user selection, the information is in the input list generated in the ui.
Then, the expression groups the data by MPAA rating, and then calculates summary statistics like means, standard deviations, and sample sizes for each level of MPAA ratings.
If we stopped here and didn’t include any of the following arguments, the app would look something like this:
But we want our table to look like this:
In order to achieve this look, we add additional arguments to our render* function.
striped = TRUE, spacing = "l", align = "lccr", digits = 4, width = "90%",
caption = "Score ratio (audience / critics' scores) summary statistics by MPAA rating."In this app, the user selects x and y variables for the scatterplot. We will extend the app to also include a textOutput which prints the correlation between the two selected variables as well some informational text.
In this code, we will attempt to
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(dplyr)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "audience_score"
),
selectInput(
inputId = "x",
label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "critics_score"
)
),
mainPanel(
plotOutput(outputId = "scatterplot"),
textOutput(outputId = "correlation")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y)) +
geom_point()
})
# Create text output stating the correlation between the two ploted
output$correlation <- renderText({
r <- round(cor(movies[, input$x], movies[, input$y], use = "pairwise"), 3)
paste0(
"Correlation = ", r,
". Note: If the relationship between the two variables is not linear, the correlation coefficient will not be meaningful."
)
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)Run the script in your R studio IDE to view and manipulate the output. You should see something like this :

We’ve seen UI outputs before, and in this section we dive deeper into the plotOutput() function to build an interactive graph.
Once again we’re going to start with this app.
And we’ll add functionality to the app so that movies corresponding to the selected points on the plot via brushing are displayed in a data table beneath the plot.
We need to make three modifications to our app to accomplish this.
Here is how to create the output that was described in the previous section, with a brush used to select entries which will appear on the data table.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(dplyr)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
br(),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y", label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "audience_score"
),
selectInput(
inputId = "x", label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "critics_score"
)
),
mainPanel(
plotOutput(outputId = "scatterplot", brush = "plot_brush"),
DT::dataTableOutput(outputId = "moviestable"),
br()
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y)) +
geom_point()
})
output$moviestable <- renderDataTable({
brushedPoints(movies, brush = input$plot_brush) %>%
select(title, audience_score, critics_score)
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)Run the script in your R studio IDE to view and manipulate the output. You should see something like this :

In addition to brushing, users can also interact with plots via hovering over them, which the following code will demonstrate.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(dplyr)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
br(),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y", label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "audience_score"
),
selectInput(
inputId = "x", label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "critics_score"
)
),
mainPanel(
plotOutput(outputId = "scatterplot", hover = "plot_hover"),
dataTableOutput(outputId = "moviestable"),
br()
)
)
)## `shiny::dataTableOutput()` is deprecated as of shiny 1.8.1.
## Please use `DT::DTOutput()` instead.
## Since you have a suitable version of DT (>= v0.32.1), shiny::dataTableOutput() will automatically use DT::DTOutput() under-the-hood.
## If this happens to break your app, set `options(shiny.legacy.datatable = TRUE)` to get the legacy datatable implementation (or `FALSE` to squelch this message).
## See <https://rstudio.github.io/DT/shiny.html> for more information.
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y)) +
geom_point()
})
output$moviestable <- renderDataTable({
nearPoints(movies, input$plot_hover) %>%
select(title, audience_score, critics_score)
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)Run the script in your R studio IDE to view and manipulate the output. You should see something like this, with the data table displaying the selected movies and details as you hover over the section of the scatter plot :

This code will develop an app where the user selects two variables and their relationship is visualized with a scatterplot, and averages of both variables are reported as well as the output of the linear regression predicting the variable on the y-axis from the variable in the x-axis.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(dplyr)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "y",
label = "Y-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "audience_score"),
selectInput(inputId = "x",
label = "X-axis:",
choices = c("imdb_rating", "imdb_num_votes", "critics_score", "audience_score", "runtime"),
selected = "critics_score")
),
# Output(s)
mainPanel(
plotOutput(outputId = "scatterplot"),
textOutput(outputId = "avg_x"), # avg of x
textOutput(outputId = "avg_y"), # avg of y
verbatimTextOutput(outputId = "lmoutput") # regression output
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y)) +
geom_point()
})
output$avg_x <- renderText({
avg_x <- movies %>% pull(input$x) %>% mean() %>% round(2)
paste("Average", input$x, "=", avg_x)
})
output$avg_y <- renderText({
avg_y <- movies %>% pull(input$y) %>% mean() %>% round(2)
paste("Average", input$y, "=", avg_y)
})
output$lmoutput <- renderPrint({
x <- movies %>% pull(input$x)
y <- movies %>% pull(input$y)
print(summary(lm(y ~ x, data = movies)), digits = 3, signif.stars = FALSE)
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)Run the script in your R studio IDE to view and manipulate the output. You should see something like this :

This code will use the downloadHandler() function in the server and downloadButton() or downloadLink() function in the UI. This would allow users to select and download the data in the fields they want.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(dplyr)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
radioButtons(inputId = "filetype",
label = "Select filetype:",
choices = c("csv", "tsv"),
selected = "csv"),
checkboxGroupInput(inputId = "selected_var",
label = "Select variables:",
choices = names(movies),
selected = c("title"))
),
mainPanel(
HTML("Select filetype and variables, then hit 'Download data'."),
downloadButton("download_data", "Download data")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
# Download file
output$download_data <- downloadHandler(
filename = function() {
paste0("movies.", input$filetype)
},
content = function(file) {
if(input$filetype == "csv"){
write_csv(movies %>% select(input$selected_var), file)
}
if(input$filetype == "tsv"){
write_tsv(movies %>% select(input$selected_var), file)
}
}
)
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)Run the script in your R studio IDE to view and manipulate the output. You should see something like this :

Scan through the code and run it to see if you can identify the various reactive elements that have been added to the features of this app.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(tools)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics Score" = "critics_score",
"Audience Score" = "audience_score",
"Runtime" = "runtime"
),
selected = "audience_score"
),
selectInput(
inputId = "x",
label = "X-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics Score" = "critics_score",
"Audience Score" = "audience_score",
"Runtime" = "runtime"
),
selected = "critics_score"
),
selectInput(
inputId = "z",
label = "Color by:",
choices = c(
"Title Type" = "title_type",
"Genre" = "genre",
"MPAA Rating" = "mpaa_rating",
"Critics Rating" = "critics_rating",
"Audience Rating" = "audience_rating"
),
selected = "mpaa_rating"
),
sliderInput(
inputId = "alpha",
label = "Alpha:",
min = 0, max = 1,
value = 0.5
),
sliderInput(
inputId = "size",
label = "Size:",
min = 0, max = 5,
value = 2
),
textInput(
inputId = "plot_title",
label = "Plot title",
placeholder = "Enter text to be used as plot title"
),
actionButton(
inputId = "update_plot_title",
label = "Update plot title"
)
),
mainPanel(
plotOutput(outputId = "scatterplot")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
new_plot_title <- eventReactive(
eventExpr = input$update_plot_title,
valueExpr = {
toTitleCase(input$plot_title)
}
)
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y, color = input$z)) +
geom_point(alpha = input$alpha, size = input$size) +
labs(title = new_plot_title())
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)Run the script in your R studio IDE to view and manipulate the output. You should see something like this :

There are many other ways of customizing the look of your app, including using custom CSS. However one quick and easy way of changing the look is using the prebuilt themes in the shinythemes package.
In order to use one of these themes, you need to load the shinythemes package first. The package website has thumbnail images of each of the themes, but it can be difficult to tell exactly how the theme will look on your app.

Pick a theme and apply to the existing app. See https://rstudio.github.io/shinythemes for more on theme options. Run the code below by changing the theme in the shinythemes line.
Possible options include
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(tools)
library(shinythemes)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(theme = shinytheme("united"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics Score" = "critics_score",
"Audience Score" = "audience_score",
"Runtime" = "runtime"
),
selected = "audience_score"
),
selectInput(
inputId = "x",
label = "X-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics Score" = "critics_score",
"Audience Score" = "audience_score",
"Runtime" = "runtime"
),
selected = "critics_score"
),
selectInput(
inputId = "z",
label = "Color by:",
choices = c(
"Title Type" = "title_type",
"Genre" = "genre",
"MPAA Rating" = "mpaa_rating",
"Critics Rating" = "critics_rating",
"Audience Rating" = "audience_rating"
),
selected = "mpaa_rating"
),
sliderInput(
inputId = "alpha",
label = "Alpha:",
min = 0, max = 1,
value = 0.5
),
sliderInput(
inputId = "size",
label = "Size:",
min = 0, max = 5,
value = 2
),
textInput(
inputId = "plot_title",
label = "Plot title",
placeholder = "Enter text to be used as plot title"
),
actionButton(
inputId = "update_plot_title",
label = "Update plot title"
)
),
mainPanel(
tags$br(),
tags$p(
"These data were obtained from",
tags$a("IMBD", href = "http://www.imbd.com/"), "and",
tags$a("Rotten Tomatoes", href = "https://www.rottentomatoes.com/"), "."
),
tags$p("The data represent", nrow(movies), "randomly sampled movies released between 1972 to 2014 in the United States."),
plotOutput(outputId = "scatterplot")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
new_plot_title <- eventReactive(
eventExpr = input$update_plot_title,
valueExpr = {
toTitleCase(input$plot_title)
}
)
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y, color = input$z)) +
geom_point(alpha = input$alpha, size = input$size) +
labs(title = new_plot_title())
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)A useful tool for browsing themes is the themeSelector() widget. To use this widget, simply add the widget to your app.
It can be inserted anywhere inside of the application, although if it is put inside a tab, it will be visible only when that tab is showing. I usually place it right underneath the fluidPage() definition.
This widget is to be used in development only. Once you decide on a theme, you should remove the widget and just define the theme you want using the shinytheme() function.
If you want to quickly test out different themes with an application, you can simply add themeSelector() somewhere to the UI. This will add a select box which lets you choose the theme. It will change the theme without having to reload or restart your app. You can see the theme selector in action by running the code below.
# Load packages ----------------------------------------------------------------
library(shiny)
library(ggplot2)
library(tools)
library(shinythemes)
# Load data --------------------------------------------------------------------
movies <- read.csv(file = "https://raw.githubusercontent.com/aaron-chen-angus/S3729C-Lesson-Files/6a58d56d3d42231fb011af462db7efc01537e515/movies.csv", header = TRUE, sep = ",")
# Define UI --------------------------------------------------------------------
ui <- fluidPage(
shinythemes::themeSelector(),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "y",
label = "Y-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics Score" = "critics_score",
"Audience Score" = "audience_score",
"Runtime" = "runtime"
),
selected = "audience_score"
),
selectInput(
inputId = "x",
label = "X-axis:",
choices = c(
"IMDB rating" = "imdb_rating",
"IMDB number of votes" = "imdb_num_votes",
"Critics Score" = "critics_score",
"Audience Score" = "audience_score",
"Runtime" = "runtime"
),
selected = "critics_score"
),
selectInput(
inputId = "z",
label = "Color by:",
choices = c(
"Title Type" = "title_type",
"Genre" = "genre",
"MPAA Rating" = "mpaa_rating",
"Critics Rating" = "critics_rating",
"Audience Rating" = "audience_rating"
),
selected = "mpaa_rating"
),
sliderInput(
inputId = "alpha",
label = "Alpha:",
min = 0, max = 1,
value = 0.5
),
sliderInput(
inputId = "size",
label = "Size:",
min = 0, max = 5,
value = 2
),
textInput(
inputId = "plot_title",
label = "Plot title",
placeholder = "Enter text to be used as plot title"
),
actionButton(
inputId = "update_plot_title",
label = "Update plot title"
)
),
mainPanel(
tags$br(),
tags$p(
"These data were obtained from",
tags$a("IMBD", href = "http://www.imbd.com/"), "and",
tags$a("Rotten Tomatoes", href = "https://www.rottentomatoes.com/"), "."
),
tags$p("The data represent", nrow(movies), "randomly sampled movies released between 1972 to 2014 in the United States."),
plotOutput(outputId = "scatterplot")
)
)
)
# Define server ----------------------------------------------------------------
server <- function(input, output, session) {
new_plot_title <- eventReactive(
eventExpr = input$update_plot_title,
valueExpr = {
toTitleCase(input$plot_title)
}
)
output$scatterplot <- renderPlot({
ggplot(data = movies, aes_string(x = input$x, y = input$y, color = input$z)) +
geom_point(alpha = input$alpha, size = input$size) +
labs(title = new_plot_title())
})
}
# Create the Shiny app object --------------------------------------------------
shinyApp(ui = ui, server = server)The bslib package provides tools for customizing Bootstrap themes directly from R, making it much easier to customize the appearance of Shiny apps & R Markdown documents.
The thematic package provides a functionality for simplified theming of ggplot2, lattice, and {base} R graphics as well as automatic theming of these plots within a Shiny app.
You can publish your shiny app on the shinyapps.io website.
This is a free space for hosting your apps. However, the freemium version has several limitations.
Step 1: Open RStudio and create a new Shiny app:
Step 2: Give it a name (without space), choose where to save it and click on the Create button:
Step 3: In the same way as when you open a new R Markdown document, the code for a basic Shiny app is created. Run the app by clicking on the Run App button to see the result:
Step 4: The basic app opens, publish it:
Step 5: If it is your first Shiny app, the box “Publish From Account” should be empty. Click on “Add New Account” to link the shinyapps.io account you just created:
Step 6: Click on the first alternative (ShinyApps.io):
Step 7: Click on the link to your ShinyApps account:
Step 8: Click on the Dashboard button to log in into your account:
Step 9: Click on your name and then on Tokens:
Step 10: If this is your first app, there should be no token already created. Create one by clicking on the Add Token button. Then Click on the Show button:
Step 11: Click on the Show Secret button:
Step 12: Now the code is complete (nothing is hidden anymore). Click on the Copy to clipboard button:
Step 13: Copy the code and click on the OK button:
Step 14: Go back to RStudio, paste the code in the console and run it:
Your computer is now authorized to deploy applications to your shinyapps.io account.
Step 15: Go back to the window where you can publish your app, choose a title (without space) and click on the Publish button:
Step 16: After several seconds (depending on the how complex your app is), the Shiny app should appear in your internet browser:
Step 17: You can now edit the app (or replace the entire code by another of your app), and run the app again by clicking on the Run App button. For this illustration, I just added a link for more information in the side panel:
Step 18: Check that the modifications have been taken into account (the link appears in the side panel as expected) and republish your app:
Step 19: Click on the Publish button:
Step 20: Your app is live! You can now the link in your assignment and with everyone else, and they will be able to access it and interact with it:
by
Hadley
Wickham
online version : https://mastering-shiny.org/
citation : Wickham, H. (2021). Mastering Shiny: Build
Interactive Apps, Reports, and Dashboards Powered by R. (1st ed).
O’Reilly
by
Hadley
Wickham
online version : https://ggplot2-book.org/
citation : Wickham, H. (2016). ggplot2: Elegant
Graphics for Data Analysis. (3rd ed). Routledge
by Winston Chang
online version : https://r-graphics.org/
citation : Chang, W. (2022). R Graphics Cookbook. (2nd
ed). O’Reilly
For your CWF Assignment, you are required to create an R Shiny App Dashboard with your own selected datasets, and enhance on the example provided as required. Hence, there are two ways that you can maximise your score for this section of the assignment.
Here are some additional matters to take note of when preparing your assignment
You can go to Kaggle to look for relevant datasets for your project implementation. Please take note that the data has to be sufficiently large (at least 200 entries) with a good combination of continuous and categorical variables.
Kaggle : https://www.kaggle.com/datasets
You can sign up for an account in Github and upload your dataset there. Here are some steps on how to set up a GitHub account and how to upload your dataset and generate the URL for linking to your R Shiny App.
GitHub : https://github.com/
Step 1 : Sign up for a GitHub Account

Step 2 : Create a New Repository

Step 3 : Click on Add File in Repository

Step 4 : Drag or Insert File Into Repository

Step 5 : Open and View the File

Step 6 : Save the Link to the Raw File for Link to R Shiny App

Thank you and have fun creating your R Shiny App !
Here are some samples of what was done by previous batches of S3729C students, where they had to create and code an app from scratch. It is less taxing now for all of you as the number of dedicated lessons has reduced, but am providing these exemplars for you to appreciate what can be done.
Please feel free to download the source code in .R files, adpat and run the codes accordingly for your own purposes. Enjoy !
Thank you !