05:00
flexdashboard is the easiest way to get started making dashboards - at its core it is an RMarkdown document template that leverages CSS flexbox (+ a lot more) to generate attractive full page layouts that are well suited for publishing multiple data visualizations and related summaries and text.
Dashboards are constructed using a RMarkdown document
Row or column based layouts can be used
Structure is specified via markdown headings
Interactivity can be added by Shiny, but it is not required
Quarto is not currently supported
demos/demo02.Rmd
---
title: "Demo 02"
output:
flexdashboard::flex_dashboard:
orientation: rows
---
```{r global}
#| include: false
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
d_city = d |>
filter(city %in% "Chicago")
```
Row {data-height=650}
-------------------------------------
### Temperature
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=temp)) +
geom_line()
```
Row {data-height=350}
-------------------------------------
### Humidity
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=humidity)) +
geom_line()
```
###
```{r}
d_city |>
mutate(
day = lubridate::wday(time, label = TRUE, abbr = FALSE),
date = as.character(lubridate::date(time))
) |>
group_by(date, day) |>
summarize(
`min` = min(temp),
`max` = max(temp),
.groups = "drop"
) |>
knitr::kable()
```
Open exercises/ex02.Rmd
, which contains the code from the previous slide, and try knitting it.
Check that you are able to successfully render the flexdashboard.
If everything is working try modifying the code:
What happens if you remove orientation: rows
from the front matter?
What happens if you change the Row
text?
What happens if you change or remove {data-height=*}
?
05:00
---
title: "Multipage layout"
output: flexdashboard::flex_dashboard
---
Page 1
=====================================
Column {data-width=600}
-------------------------------------
### Chart 1
```{r}
```
Column {data-width=400}
-------------------------------------
### Chart 2
```{r}
```
### Chart 3
```{r}
```
Page 2 {data-orientation=rows}
=====================================
Row {data-height=600}
-------------------------------------
### Chart 4
```{r}
```
Row {data-height=400}
-------------------------------------
### Chart 5
```{r}
```
### Chart 6
```{r}
```
From the preceding examples it is relatively straightforward to see how markdown is translated into the flexdashboard’s layout,
Heading | Purpose | |
---|---|---|
Level 1 | # or ======= |
Pages |
Level 2 | ## or ------ |
Columns or rows1 |
Level 3 | ### |
Chart or output elements2 |
Horizontal Rules | *** , --- , or ___ |
Separate chart from commentary3 |
For the sharp-eyed among you, you may have noticed certain headings were given additional attributes via arguments wrapped in {}
. These are CSS attributes that modify the display behavior of the elements they are attached to. Some common attribues,
data-height
and data-width
control the relative size of elements
data-padding
or .no-padding
control the padding around elements in pixels
data-orientation
can be applied to pages to alter the orientation for a specific page
.tabset
indicates a column or row should be composed on tabset elements
.sidebar
indicates a sidebar should be included (local or global)
In the previous layout examples we saw the used some document options in the front matter, a couple of commonly used options that are worth knowning about:
orientation
- default is columns
, determines element layout orientation
vertical_layout
- default is fill
but scroll
can be used to extend the viewable area
self_contained
- default is TRUE
, embeds all assessts within the html document (e.g. scripts, stylesheets, images, and videos)
theme
- specifies a theme to use for styling (more on this later)
navbar
- constructs a navigation bar at the top of the screen
We’ve just seen a number of possible layout methods for a flexdashboard, lets return to the code we’ve seen previously, provided in exercises/ex03.Rmd
, and try changing layout more purposefully this time (e.g. a column, multipage, or storyboard layout).
Use this time to experiment with the different layout options and see what seems to work best.
Try out a multipage or storyboard layout
Try making a reasonable looking column layout
05:00
Because flexdashboards are just RMarkdown documents - we can leverage the parameterized reports functionality to pass in arguments (e.g. choice of city) while still keeping the final document static.
This is done by declaring the parameters in the front matter using the params
field.
The values are then accessed via a read-only list called params
within the report’s R chunks.
demos/demo03-1.Rmd
---
title: "Demo 03 - Part 1"
output:
flexdashboard::flex_dashboard:
orientation: rows
params:
city: "Los Angeles"
---
```{r setup}
#| include: false
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
d_city = d |>
filter(city %in% params$city)
```
Row {data-height=650}
-------------------------------------
### Temperature
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=temp)) +
ggtitle(params$city) +
geom_line()
```
Row {data-height=350}
-------------------------------------
### Humidity
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=humidity)) +
geom_line()
```
###
```{r}
d_city |>
mutate(
day = lubridate::wday(time, label = TRUE, abbr = FALSE),
date = as.character(lubridate::date(time))
) |>
group_by(date, day) |>
summarize(
`min` = min(temp),
`max` = max(temp),
.groups = "drop"
) |>
knitr::kable()
```
demos/demo03-2.Rmd
---
title: "Demo 03 - Part 2"
output:
flexdashboard::flex_dashboard:
orientation: rows
params:
city:
label: "City"
value: "Chicago"
input: select
choices: ["Chicago", "Durham", "Sedona", "New York",
"Los Angeles", "Seattle", "Omaha"]
---
```{r setup}
#| include: false
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
d_city = d |>
filter(city %in% params$city)
```
Row {data-height=650}
-------------------------------------
### Temperature
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=temp)) +
ggtitle(params$city) +
geom_line()
```
Row {data-height=350}
-------------------------------------
### Humidity
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=humidity)) +
geom_line()
```
###
```{r}
d_city |>
mutate(
day = lubridate::wday(time, label = TRUE, abbr = FALSE),
date = as.character(lubridate::date(time))
) |>
group_by(date, day) |>
summarize(
`min` = min(temp),
`max` = max(temp),
.groups = "drop"
) |>
knitr::kable()
```
We can use Shiny components and reactivity in a flexdashboard (or any html output based RMarkdown document) by including runtime: shiny
in the front matter.
This results in the document being served by shiny (which has implications for sharing and publishing)
demos/demo04.Rmd
---
title: "Demo 04"
output:
flexdashboard::flex_dashboard
runtime: shiny
---
```{r global}
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
```
```{r}
d_vars = d |>
select(where(is.numeric)) |>
names()
d_city = reactive({
d |>
filter(city %in% input$city)
})
```
Inputs {.sidebar}
-------------------------------------
```{r}
selectInput(
"city", "Select a city",
choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles")
)
selectInput(
"var", "Select a variable",
choices = d_vars, selected = "humidity"
)
```
Col
-------------------------------------
### Temperature
```{r}
renderPlot({
d_city() |>
ggplot(aes(x=time, y=temp)) +
ggtitle(input$city) +
geom_line()
})
```
Col
-------------------------------------
### Other
```{r}
renderPlot({
d_city() |>
ggplot(aes(x=time, y=.data[[input$var]])) +
geom_line()
})
```
The shiny inputs do not need to live in a sidepanel - try rewriting Demo 04’s code such that the inputs for city and variable are located within the temperature and other elements respectively.
The base code is provided in exercises/ex04.Rmd
, if you have extra time try playing around with the specific positioning of the input elements.
Hint - if you are having difficulty with your plots fitting on screen you can adjust their size via renderPlot()
s width
and height
arguments.
05:00
flexdashboard
provides two built-in html components that can be included in your dashboard:
Value boxes:
Gauges:
Either component can be included in the dashboard with a static value via directly calling valueBox()
or gauge()
Shiny reactive variants can be implemented using valueBoxOutput()
with renderValueBox()
or gaugeOutput()
with renderGauge()
Both components take a color argument, this can be either
One of the standard bootstrap theme color names (i.e. “success”, “warning”, “danger”, “primary”, or “info”)
or any other valid CSS color specifier
Value box icons should use names from Font Awesome, Ionicons, or Bootstrap Glyphicons
demos/demo05.Rmd
---
title: "Demo 05 - value boxes and gauges"
output:
flexdashboard::flex_dashboard
runtime: shiny
---
```{r global}
library(tidyverse)
library(flexdashboard)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
```
```{r}
d_vars = d |>
select(where(is.numeric)) |>
names()
d_city = reactive({
d |>
filter(city %in% input$city)
})
```
Col {data-width=800}
-------------------------------------
###
```{r}
selectInput(
"city", "Select a city",
choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles")
)
renderPlot({
d_city() |>
ggplot(aes(x=time, y=temp)) +
geom_line()
}, height = 600)
```
Col {data-width=200}
-------------------------------------
### Min temperature
```{r}
renderGauge({
gauge(
min(d_city()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Max temperature
```{r}
renderGauge({
gauge(
max(d_city()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Avg temperature
```{r}
renderValueBox({
avg = mean(d_city()$temp) |> round(1)
valueBox(
avg,
caption = "Avg temp",
icon = "fa-thermometer-half",
color = case_when(
avg >= 0 & avg < 50 ~ "warning",
avg >=50 & avg < 90 ~ "success",
avg >=90 & avg < 120 ~ "danger"
)
)
})
```
Modify the code provide in ex05.Rmd
(based on demo05.R
) so that the user is able to select different data features (e.g. humidity, feelslike, etc.) and have that change reflected in the plot, value boxes, and gauges.
Some guidance:
Think about the choice of components given the additional flexibility we have added
If you want to add reactive text to a heading you use an inline code chunk, e.g.
08:00
This has nothing in particular to do with flexdashboard but is a super useful Shiny technique for improving interactivity.
Shiny’s plotOutput()
s can also be used to generate inputs based on user click events. Here we are using the brush rectangular selection to subset the data and then updating the relevant components.
demos/demo06.Rmd
---
title: "Demo 06 - Linked brushing"
output:
flexdashboard::flex_dashboard
runtime: shiny
---
```{r global}
library(tidyverse)
library(flexdashboard)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
```
```{r}
d_vars = d |>
select(where(is.numeric)) |>
names()
d_city = reactive({
d |>
filter(city %in% input$city)
})
d_selected = reactive({
db = shiny::brushedPoints(d_city(), input$plot_brush)
if (nrow(db) == 0)
db = d_city()
db
})
```
Col {data-width=800}
-------------------------------------
###
```{r}
selectInput(
"city", "Select a city",
choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles")
)
renderPlot(
{
d_city() |>
ggplot(aes(x=time, y=temp)) +
geom_line()
},
outputArgs = list(
brush = shiny::brushOpts(id = "plot_brush")
),
height = 600
)
```
Col {data-width=200}
-------------------------------------
### Starting time
```{r}
renderValueBox({
valueBox(
min(d_selected()$time),
caption = "Starting time",
icon = "fa-calendar-days"
)
})
```
### Ending time
```{r}
renderValueBox({
valueBox(
max(d_selected()$time),
caption = "Ending time",
icon = "fa-calendar-days"
)
})
```
### Min temperature
```{r}
renderGauge({
gauge(
min(d_selected()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Max temperature
```{r}
renderGauge({
gauge(
max(d_selected()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Avg temperature
```{r}
renderValueBox({
avg = mean(d_selected()$temp) |> round(1)
valueBox(
avg,
caption = "Avg temp",
icon = "fa-thermometer-half",
color = case_when(
avg >= 0 & avg < 50 ~ "warning",
avg >=50 & avg < 90 ~ "success",
avg >=90 & avg < 120 ~ "danger"
)
)
})
```
posit::conf 2023 - Shiny Dashboards