library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(ROvis.plotly) # nolint: missing_package_linter.
library(ROvis.utils)Introduction
This vignette demonstrates how to create clear, publication-ready visualizations in R using the plotly package, styled according to the Rijksoverheids style The provided functions allow you to quickly apply consistent fonts, colors, grid lines, axes, legends, tooltips, and annotation styling across all your Plotly charts.
Key Functions
ro_ply_theme()
Returns a list of theming options (colors, fonts, grid, axis, legend, tooltip styles, etc.) based on the Rijksoverheid visual identity. You can customize many aspects, such as the color palette, fonts, grid lines, and axis formatting.ro_ply_add_theme()
Applies the Rijksoverheid / RIVM theme (fromro_ply_theme()) to any Plotly figure.ro_ply_title()
Adds a custom, left-aligned main title and axis labels as annotations, in line with the Rijksoverheid / RIVM style.ro_ply_cursor()
Changes the mouse cursor in interactive Plotly charts from a crosshair to a standard arrow for a friendlier user experience.ro_ply_legend_theme()
Enhances the legend styling by adding boxes around legend items.ro_ply_dim()
Ensures all charts have the correct dimensions for Rijksoverheid / RIVM reports, and are responsive in Shiny applications.
Code Examples
Column chart
This example shows how to create a grouped vertical bar chart (column chart) displaying the number of cases by age group and sex.
The Rijksoverheid / RIVM theme is set with: theme_rivm <- ro_ply_theme(barmode = "group") where barmode = "group" ensures grouped bars for each age group.
age_levels <- c(
"0", "1-9", "10-19", "20-29",
"30-39", "40-49", "50-59",
"60-69", "70-79", "80-89", "90+"
)
base_data <- tidyr::expand_grid(
`Age group` = age_levels,
Sex = c("Men", "Women")
) |>
dplyr::mutate(
`Number of Cases` = sample(1:50, length(age_levels) * 2, TRUE)
)
summary_data <- base_data |>
dplyr::summarize(
`Number of Cases` = sum(`Number of Cases`),
.by = `Age group`
) |>
dplyr::mutate(Sex = "Total")
data <- dplyr::bind_rows(base_data, summary_data) |>
dplyr::mutate(
`Age group` = factor(`Age group`, levels = age_levels),
Sex = factor(Sex, levels = c("Men", "Women", "Total"))
)
theme_rivm <- ro_ply_theme(barmode = "group")
kleuren <- c(theme_rivm$colorway[1], theme_rivm$colorway[3], theme_rivm$colorway[4])
fig <- plot_ly(
data = data,
x = ~`Age group`,
y = ~`Number of Cases`,
type = "bar",
color = ~Sex,
colors = kleuren,
text = ~paste0(
"Geslacht: <b>", Sex, "</b>",
"<br>Leeftijdsgroep: <b>", `Age group`,
"</b><br>Aantal cases: <b>",
format(round(`Number of Cases`, 0), big.mark = ".", decimal.mark = ",", scientific = FALSE), "</b>"
),
textposition = "none",
hoverinfo = "text"
)
fig <- ro_ply_add_theme(fig = fig, theme = theme_rivm)
fig <- ro_ply_title(fig, y_data = data$"Age group", y_label = "cases", title = "fancy title", x_label = "groups")
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
figBar Graph
This example demonstrates a simple vertical bar chart, showing the number of cases per sex, with the default Rijksoverheid / RIVM theme.
# Create example data: counts per group
bar_data <- tibble::tibble(
Sex = c("Man", "Vrouw"),
n = c(1234, 1675)
)
# Use the color function for Rijksoverheid / RIVM colors
sex_colors <- c(
"Man" = ro_color("hemelblauw"),
"Vrouw" = ro_color("robijnrood")
)
# Create plotly bar chart
fig <- plot_ly(
data = bar_data,
x = ~Sex,
y = ~n,
type = "bar",
color = ~Sex,
colors = sex_colors,
text = ~paste0(
"Geslacht: <b>", Sex, "</b>",
"<br>Aantal cases: <b>",
format(round(n, 0), big.mark = ".", decimal.mark = ",", scientific = FALSE), "</b>"
),
hoverinfo = "text"
)
# Apply Rijksoverheid / RIVM theme
fig <- ro_ply_add_theme(fig, ro_ply_theme())
# Add custom title and axis labels
fig <- ro_ply_title(
fig,
y_data = bar_data$n,
y_label = "Aantal cases",
title = "COVID-19 cases per geslacht",
x_label = "Geslacht"
)
# Apply cursor and legend theming
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
figHorizontal Bar Chart
This example demonstrates how to create a horizontal bar chart grouped by age group and sex.
The Rijksoverheid / RIVM theme is customized to: - show a spike line on the y-axis only, - show the y-axis line and hide the x-axis line, - show grid lines only for the x-axis.
# Generate random data
data <- tibble::tibble(expand.grid(`Age group` = c("A", "B", "C", "D", "E"),
`Sex` = c("Man", "Vrouw")),
`Number of Cases` = sample(1:50, 10, TRUE))
theme_rivm <- ro_ply_theme(y_axis_spike = TRUE,
x_axis_spike = FALSE,
y_axis_showline = TRUE,
x_axis_showline = FALSE,
y_axis_showgrid = FALSE,
x_axis_showgrid = TRUE)
kleuren <- c(theme_rivm$colorway[1], theme_rivm$colorway[3])
fig <- plot_ly() |>
plotly::add_trace(data = data,
x = ~`Number of Cases`,
y = ~`Age group`,
color = ~`Sex`,
colors = kleuren,
type = "bar",
orientation = "h", # horizontal bars
hovertext = ~paste0(
"Leeftijdsgroep: <b>", `Age group`, "</b><br>",
"Geslacht: <b>", `Sex`, "</b><br>",
"Aantal cases: <b>", `Number of Cases`, "</b>"
),
hoverinfo = "text"
)
fig <- ro_ply_add_theme(fig = fig, theme = theme_rivm)
fig <- ro_ply_title(fig, y_data = data$"Age group", y_label = "group", title = "fancy title", x_label = "cases")
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
figStacked Bar Graph
This example demonstrates how to create a stacked bar chart displays the number of cases per age group and sex.
The theme uses: theme_rivm <- ro_ply_theme(barmode = "stack") where barmode = "stack" stacks the bars by sex within each age group.
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
# Example data: number of cases per age group and sex
stacked_data <- tibble::tibble(
Agegroup = rep(c("0-9", "10-19", "20-29", "30-39"), each = 3),
Sex = rep(c("Man", "Vrouw", "Onbekend"), times = 4),
n = c(10, 14, 2, 15, 17, 1, 23, 18, 0, 9, 11, 3)
)
# Define color palette using your color function
sex_colors <- c(
"Man" = ro_color("hemelblauw"),
"Vrouw" = ro_color("robijnrood"),
"Onbekend" = ro_color("donkergeel")
)
# Use ro_ply_theme with barmode = "stack"
theme_rivm <- ro_ply_theme(barmode = "stack")
# Create the plotly stacked bar chart
fig <- plot_ly(
data = stacked_data,
x = ~Agegroup,
y = ~n,
type = "bar",
color = ~Sex,
colors = sex_colors,
text = ~paste0(
"Leeftijdsgroep: <b>", Agegroup, "</b>",
"<br>Geslacht: <b>", Sex, "</b>",
"<br>Aantal cases: <b>",
format(round(n, 0), big.mark = ".", decimal.mark = ",", scientific = FALSE), "</b>"
),
hoverinfo = "text"
)
fig <- ro_ply_add_theme(fig, theme_rivm)
fig <- ro_ply_title(
fig,
y_data = stacked_data |> group_by(Agegroup) |> summarise(n_total = sum(n)) |> pull(n_total),
y_label = "Aantal cases",
title = "COVID-19 cases per leeftijdsgroep en geslacht",
x_label = "Leeftijdsgroep"
)
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
figProportional Stacked Bar Graph
This example demonstrates how to create aroportional stacked bar chart, displaying the proportion of cases per age group and sex as a horizontal.
The theme uses: theme_rivm <- ro_ply_theme(barmode = "stack") where barmode = "stack" stacks the bars by sex within each age group.
library(dplyr)
# Example data: Age group, Sex, and number of cases
prop_data <- tibble::tibble(
age_cat = rep(c("0-9", "10-19", "20-29", "30-39"), each = 2),
Sex = rep(c("Man", "Vrouw"), times = 4),
n = c(12, 8, 15, 12, 20, 18, 10, 7)
) |>
group_by(age_cat) |>
mutate(
total = sum(n),
prop = n / total,
prop_label = paste0(round(prop * 100, 1), "%"),
bar_label = age_cat
) |>
ungroup()
# Define color palette using your color function
sex_colors <- c(
"Man" = ro_color("hemelblauw"),
"Vrouw" = ro_color("robijnrood")
)
# Use ro_ply_theme with barmode = "stack"
theme_rivm <- ro_ply_theme(barmode = "stack")
# Create the proportional stacked bar chart (horizontal)
fig <- plot_ly(
data = prop_data,
y = ~bar_label,
x = ~prop,
color = ~Sex,
colors = sex_colors,
type = "bar",
orientation = "h",
text = ~paste0(
"Leeftijdsgroep: <b>", age_cat, "</b><br>",
"Geslacht: <b>", Sex, "</b><br>",
"Percentage: <b>", prop_label, "</b>"
),
textposition = "auto",
hovertemplate = ~paste0(
"Leeftijdsgroep: <b>", age_cat, "</b><br>",
"Geslacht: <b>", Sex, "</b><br>",
"Percentage: <b>", prop_label, "</b>"
),
hoverinfo = "text"
)
fig <- ro_ply_add_theme(fig, theme_rivm)
fig <- ro_ply_title(
fig,
y_data = prop_data$bar_label,
y_label = "Leeftijdsgroep",
title = "Percentage COVID-19 cases per leeftijdsgroep en geslacht",
x_label = "Percentage"
)
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
# Format x-axis as percentage
fig <- fig |>
layout(
xaxis = list(
tickformat = ".0%",
range = c(0, 1),
title = "Percentage"
)
)
figTrendline
This example shows a trendline (with points and a moving average) for daily case counts.
Here, the x-axis is formatted to display as “day month” for better readability: theme_rivm <- ro_ply_theme(x_axis_tick_format = "%d %b") where x_axis_tick_format customizes the date format on the x-axis.
library(dplyr)
library(zoo)
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
library(stringr)
# Example data: daily cases with a 7-day rolling average
tab_cases_rolling_average <- tibble::tibble(
Date_statistics = seq(as.Date("2022-11-01"), as.Date("2022-12-31"), by = "day"),
n = round(seq(100, 400, length.out = 61) + rnorm(61, 0, 20)) # trend + noise
) |>
mutate(
moving_average = zoo::rollmean(n, k = 7, fill = NA, align = "right"),
text = str_c(
"Dag: <b>", format(Date_statistics, "%d-%m-%Y"), "</b><br>",
"Aantal cases: <b>", format(n, big.mark = ".", decimal.mark = ",", scientific = FALSE), "</b><br>",
"7-daags gemiddelde: <b>", format(round(moving_average, 0),
big.mark = ".", decimal.mark = ",", scientific = FALSE), "</b>"
)
)
theme_rivm <- ro_ply_theme(x_axis_tick_format = "%d %b")
fig <- plot_ly(
data = tab_cases_rolling_average,
x = ~Date_statistics
) |>
plotly::add_markers(
y = ~n,
name = "Aantal cases",
marker = list(color = ro_color("robijnrood_tint60"), size = 5),
text = ~text,
hoverinfo = "text"
) |>
plotly::add_lines(
y = ~moving_average,
name = "7-daags gemiddelde",
line = list(color = ro_color("robijnrood"), width = 2),
text = ~text,
hoverinfo = "text"
)
fig <- ro_ply_add_theme(fig, theme_rivm)
fig <- ro_ply_title(
fig,
y_data = tab_cases_rolling_average$n,
y_label = "Aantal gevallen",
title = "COVID-19 gevallen per dag en 7-daags gemiddelde",
x_label = "Datum"
)
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
figGrouped Trendline
This example shows a grouped trendline for multiple provinces.
Here, the x-axis is formatted to display as “day month” for better readability: theme_rivm <- ro_ply_theme(x_axis_tick_format = "%d %b") where x_axis_tick_format customizes the date format on the x-axis.
library(dplyr)
# Example: Simulated COVID-19 cases per week for 2 provinces
weeks <- seq(as.Date("2022-11-01"), as.Date("2022-12-31"), by = "week")
tab_cases_week_by_prov <- tibble::tibble(
week = rep(weeks, 2),
Province = rep(c("Groningen", "Zeeland"), each = length(weeks)),
n = c(
seq(100, 200, length.out = length(weeks)) + rnorm(length(weeks), 0, 10), # Slightly increasing with noise
seq(100, 200, length.out = length(weeks)) + rnorm(length(weeks), 0, 10)
)
)
theme_rivm <- ro_ply_theme(x_axis_tick_format = "%d %b")
prov_names <- unique(tab_cases_week_by_prov$Province)
prov_colors <- setNames(theme_rivm$colorway[seq_along(prov_names)], prov_names)
fig <- plot_ly()
for (prov in prov_names) {
prov_data <- filter(tab_cases_week_by_prov, Province == prov)
fig <- fig |>
plotly::add_trace(
data = prov_data,
x = ~week,
y = ~n,
type = "scatter",
mode = "lines", # Or use mode = "lines+markers" for lines and points
name = prov,
line = list(color = prov_colors[prov], width = 2),
text = ~paste0(
"Provincie: <b>", Province, "</b>",
"<br>Week: <b>", format(week, "%e %b %Y"), "</b>",
"<br>Aantal cases: <b>", format(round(n, 0), big.mark = ".", decimal.mark = ",", scientific = FALSE), "</b>"
),
hoverinfo = "text"
)
}
fig <- ro_ply_add_theme(fig, theme_rivm)
fig <- ro_ply_title(
fig,
y_data = tab_cases_week_by_prov$n,
y_label = "Aantal gevallen",
title = "COVID-19 gevallen per week voor 5 provincies",
x_label = "Week"
)
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
figLine chart
This example creates a grouped line chart showing cases by year and sex. The theme is set to display the x-axis as a plain number (year), without the default thousand separator: theme_rivm <- ro_ply_theme(x_axis_tick_format = ".0f") so years appear as 2001, 2002, etc.
library(dplyr)
library(stringr)
# Generate random data
data <- tibble::tibble(
expand.grid(Jaar = 2001:2020, Geslacht = c("Man", "Vrouw")),
value = sample(1:50, 40, TRUE)
)
data_sum <- data |>
group_by(Jaar) |>
summarize(value = sum(value), .groups = "drop") |>
mutate(Geslacht = "Totaal")
data_all <- bind_rows(data, data_sum) |>
mutate(Geslacht = factor(Geslacht, levels = c("Man", "Vrouw", "Totaal")))
# Use Rijksoverheid / RIVM theme and color palette
theme_rivm <- ro_ply_theme(x_axis_tick_format = ".0f") # No thousand seperator for the x-axis
kleuren <- c(
"Man" = theme_rivm$colorway[1],
"Vrouw" = theme_rivm$colorway[3],
"Totaal" = theme_rivm$colorway[4]
)
# Create the plotly line chart
fig <- plot_ly()
for (geslacht in levels(data_all$Geslacht)) {
subset <- filter(data_all, Geslacht == geslacht)
fig <- fig |>
plotly::add_trace(
data = subset,
x = ~Jaar,
y = ~value,
type = "scatter",
mode = "lines",
name = geslacht,
line = list(color = kleuren[geslacht], width = 2),
text = ~str_c(
"Geslacht: <b>", Geslacht,
"</b><br>Jaar: <b>", Jaar,
"</b><br>Aantal cases: <b>", value
),
hoverinfo = "text"
)
}
fig <- ro_ply_add_theme(fig, theme = theme_rivm)
fig <- ro_ply_title(
fig,
y_data = data_all$value,
y_label = "Aantal cases",
title = "COVID-19 cases per geslacht",
x_label = "" #For the year, no x-axis title is needed
)
fig <- ro_ply_cursor(fig)
fig <- ro_ply_legend_theme(fig)
fig <- ro_ply_dim(fig)
fig