library(ggplot2)
library(dplyr)
library(ROvis.utils) # nolint: missing_package_linter.
library(ROvis.ggplot2) # nolint: missing_package_linter.This vignette walks you through the steps to create a static graph with ggplot2 according to a fixed set of rules/guidelines. This vignette uses the RIVM guidelines as a basis. In future releases of ROvis, we aim to allow adding other guidelines from other Rijksoverheid organisations as well.
Important
In this vignette, the examples specifically follow the style guidelines of the National Institue for Public Health and the Envrionment (RIVM).
library(ggplot2)
library(dplyr)
library(ROvis.utils) # nolint: missing_package_linter.
library(ROvis.ggplot2) # nolint: missing_package_linter.We use a small synthetic dataset for demonstration purposes:
sex_levels <- c("Women", "Men", "Total")
# Reported cases per age group and sex
dat_cases <- data.frame(
agegroup = rep(c("0-19", "20-39", "40-59", "60-79", "80+"), times = 3),
sex = factor(rep(sex_levels, each = 5), levels = sex_levels),
n = c(
12000,
48000,
61000,
43000,
18000,
10000,
45000,
58000,
39000,
14000,
22000,
93000,
119000,
82000,
32000
)
)
# Reported cases per week (three series)
dat_weekly <- data.frame(
week = factor(rep(1:12, times = 3), levels = 1:12),
sex = factor(rep(sex_levels, each = 12), levels = sex_levels),
n = c(
800,
950,
1100,
1400,
1600,
1550,
1300,
1100,
900,
750,
600,
500,
700,
880,
1020,
1300,
1480,
1420,
1180,
990,
820,
680,
540,
440,
1500,
1830,
2120,
2700,
3080,
2970,
2480,
2090,
1720,
1430,
1140,
940
)
)
# Reported cases per day of the week
dat_weekday <- data.frame(
day = factor(
c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"),
levels = c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
),
n = c(180, 165, 172, 190, 210, 95, 60)
)ro_gg_plot is designed to be a complete add-on for your ggplot2 graphs. It’s more than a theme. It applies a lower-level theme (ro_gg_theme), and makes adjustments on top of that.
Unlike ro_gg_theme(), which returns a theme() object you add with + anywhere in the chain, ro_gg_plot() needs to see the whole plot (its data, geoms, and scales) to decide what to add. So it takes a complete ggplot2 object as input and returns a modified one. Build your plot fully first, save it to an object, and call ro_gg_plot() on that object as the very last step:
p_basic <- dat_cases |>
filter(sex == "Women") |>
ggplot(aes(x = agegroup, y = n)) +
geom_col(fill = ro_color("hemelblauw")) +
labs(
title = "Cases by age group",
subtitle = "Women only",
x = "Age class",
y = "Value"
)
ro_gg_plot(p_basic)! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
Keep in mind that when using ro_gg_theme, you need to take additional coding steps to fully comply
with the RIVM style. For more information, see the ROvis documentation website:
https://rivm-syso.github.io/ROvis
A note on piping: you cannot pipe straight out of a + chain into ro_gg_plot(), because |> binds tighter than +. Writing
ggplot(...) +
geom_col() +
labs(...) |>
ro_gg_plot()only applies ro_gg_plot() to the result of labs(...), not to the whole plot (and will error, since that’s not a ggplot object). Always assign the finished plot to a variable first!
p_grouped <- dat_cases |>
filter(sex != "Total") |>
ggplot(aes(x = agegroup, y = n, fill = sex)) +
geom_col(position = "dodge") +
scale_fill_manual(
values = c(Women = ro_color("robijnrood"), Men = ro_color("hemelblauw"))
) +
labs(
title = "Cases by age group and sex",
x = "Age class",
y = "Value",
fill = "Sex"
)
ro_gg_plot(p_grouped)! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
Notice that we set fill = "Sex" in labs(), but no legend title is shown: RO style never displays one, so ro_gg_plot() removes it automatically.
ro_gg_plot() also fixes up the bar/column styling to match RO style: each group of bars occupies 80% of its category slot (the remaining 20% is the gap between groups), bars within a group sit flush against each other with no gap, and bars get no outline. The x-axis ticks are moved from under each category to the boundary between groups — that’s the small tick marks you can see between “0-19”/“20-39”/“40-59” above, instead of one centered under each category.
Any discrete x-axis works the same:
p_weekday <- dat_weekday |>
ggplot(aes(x = day, y = n)) +
geom_col(fill = ro_color("hemelblauw")) +
labs(
title = "Cases by day of the week",
x = "Day",
y = "Value"
)
ro_gg_plot(p_weekday)! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
The group ticks sit at the boundary between each day, same as for the age-group chart above.
For a horizontal bar chart, swap x and y in aes() and set flip = TRUE:
p_horizontal <- dat_cases |>
filter(sex == "Women") |>
ggplot(aes(x = n, y = agegroup)) +
geom_col(fill = ro_color("hemelblauw")) +
labs(
title = "Cases by age group",
subtitle = "Women only",
x = "Value",
y = "Age class"
)
ro_gg_plot(p_horizontal, flip = TRUE)! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
p_line <- dat_weekly |>
filter(sex != "Total") |>
ggplot(aes(x = week, y = n, color = sex, group = sex)) +
geom_line(linewidth = 1) +
scale_color_manual(
values = c(Women = ro_color("robijnrood"), Men = ro_color("hemelblauw"))
) +
labs(
title = "Weekly cases by sex",
x = "Week",
y = "Value",
color = "Sex"
)
ro_gg_plot(p_line)! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
ro_gg_plot() detects that this is not a bar/column chart, so it leaves ggplot2’s default axis expansion and limits alone (a line chart usually shouldn’t touch the x-axis the way a bar chart does): it only adds the thousands separator to the y-axis labels and still removes the legend title and relocates the y-axis title.
ro_gg_plot() does not choose colors for you. You can use ROvis.utils::ro_color() or ROvis.utils::ro_color_palette() in your scale_fill_manual()/scale_color_manual() calls, exactly as you would without it:
{r palettes, fig.height=3} # ROvis.utils::ro_show_colors("categorical") # ROvis.utils::ro_show_colors("gender_con") #ro_gg_plot()ro_gg_plot() returns a normal ggplot2 object, so you can keep adding to it with +. Things like colors, text sizes, legend position, labs(), coord_*(), and so on behave exactly as they would on any other plot, because later theme()/labs() calls simply override the earlier ones.
ro_gg_plot(p_basic) +
theme(plot.title = element_text(size = 16, face = "bold", color = "red"))! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
So, making changes on top, or overriding them completely, is no problem. However, there are some things to take into account, which we will discuss below.
ro_gg_y_title() (called internally) blanks out the normal axis.title.y and replaces it with a text grob (text graphical object). A text grob is just a piece of text drawn at a specific position. In this case, that is at a specific y-coordinate, namely the height of the top gridline at the moment ro_gg_plot() was called. Because of this, theme(axis.title.y = ...) calls added afterward have no effect, because there’s no axis title left to style, only that positioned grob.
Most of the time it works wonderful, but in some cases it might not. If the default position doesn’t work for your plot, set auto_set_y_title = FALSE to disable the text grob completely. ro_gg_plot() then still applies the cosmetic theme, legend cleanup, and axis changes, but leaves the y-axis title as a normal, styleable axis.title.y element:
ro_gg_plot(p_basic, auto_set_y_title = FALSE) +
theme(axis.title.y = element_text(angle = 0, vjust = 1.05, hjust = 0))! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
Axis limits keep adjusting to new layers, because ro_gg_plot() sets limits = ro_gg_axis, a function ggplot2 re-evaluates against the full data range at draw time. Adding a geom_hline(), another geom_col(), etc. after ro_gg_plot() correctly widens the axis.
The y-axis title will not follow, though: it’s the fixed coordinate described above, computed once from the top gridline at call time. If a later layer pushes the top gridline higher, the title ends up positioned too low. This is why we recommend to call ro_gg_plot() last, after all layers, scales, and labels have been added. If you do need to add data afterward, it’s best to use auto_set_y_title = FALSE within ro_gg_plot().
The same principle applies to legend titles: ro_gg_plot() blanks the legend title only for aes or layers that are present when it’s called. A scale_*() or layer added afterwards, introduces a new legend. This legend will keep the default title, since the removal step has already run. Normally this shouldn’t be an issue though. As, again, we advise to call ro_gg_plot() last, after all layers, scales, and labels have been added.
ro_gg_plot() aims to automate as much as possible to avoid many manual adjustments. A few things are still manual:
ro_gg_plot(). If you need different values, apply ro_gg_group_ticks() and the geom_col()/geom_bar() parameters yourself instead.plot$scales$scales) that aren’t part of the stable, exported API, so this could break in a future ggplot2 release.If you’ve already added your own scale_y_continuous()/scale_x_continuous() for the continuous axis, ro_gg_plot() leaves it untouched rather than overriding it.
It is possible to use the lower-level ro_gg_theme as a standalone theme. This does not give you the “full” set of changes, but it gives you a bit more flexibility in what you would like to add (as far as ro_gg_plot doesn’t allow you already):
p_basic <- dat_cases |>
filter(sex == "Women") |>
ggplot(aes(x = agegroup, y = n)) +
geom_col(fill = ro_color("hemelblauw")) +
labs(
title = "Cases by age group",
subtitle = "Women only",
x = "Age class",
y = "Value"
) +
ro_gg_theme()! Can't find the `base_family` = "RijksoverheidSansWebText" in installed fonts,
and neither fall-back fonts, "Verdana" and "Arial", are installed.
ℹ Falling back to "URW Gothic", the first font found on this system. Check
installed system fonts with `systemfonts::system_fonts()`.
p_basicAs you can see, this is pretty bare bones. Typography and colors are applied, but you miss things like thousand separators, axis label positioning, tick mark positioning and bar placement. That why we recommend using ro_gg_plot instead: this fixes all those shortcomings.
Use ro_gg_save() to save your figure for offline publication:
ro_gg_save("p_grouped.png", ro_gg_plot(p_grouped))