--- title: "Background" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Background} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## The problem The `scales` package provides a family of easy to use functions like `scales::label_number` or `scales::label_percent` to format numbers in decimal format, as percentages or currencies, which could also be passed to the `labels` argument of `ggplot2`s family of scale functions to format axes or legends. However, let's say you have to prepare a report or charts where you have to use country-specific style conventions to display numbers which differ from the default anglo-american style. While that could be achieved with the `scales` package as well, in practice formatting numbers and styling axes quickly becomes cumbersome and annoying as you always have to * set the decimal and grouping marks (if deviating from the defaults) so you end up with ``` scales::label_number(big.mark = ".", decimal.mark = ",")(x) ``` to format a number `x` in decimal format according to style conventions found in many European countries. * provide a suitable labeller function to the `labels` argument of `scale_xxx_yyy` so you end up with ``` scale_x_continuous( labels = scales::label_percent(big.mark = ".", decimal.mark = ",") ) ``` to display numbers on the x axis as percentages using the style convention found in many European countries. As of `scales` 1.4.0 (released 2025-04-24), you no longer have to repeat `big.mark`/`decimal.mark` on every single call within a session — a new `scales::number_options(decimal.mark = ",", big.mark = ".")` sets these (and `style_positive`/`style_negative`, and currency-specific equivalents) as global defaults for every subsequent `label_number()`/`label_currency()` call. That closes part of the gap above for a *single* country's conventions applied for the rest of a session — but it's still one global default at a time. It doesn't help if you need several countries' conventions side by side in the same report or chart (like the G20 example in the [README](https://trekonom.github.io/countryscales/)), and it doesn't touch the harder problems `countryscales` also handles under the hood: correct currency symbol/sign positioning and locale-correct percent-sign placement and spacing. ## The solution A first and simple solution to this problem would be to add some simple wrappers at the beginning of your R script or R markdown document like ``` my_label_number <- function() { scales::label_number(big.mark = ".", decimal.mark = ",") } ``` But then you end up copy and pasting from one report to the next, so sooner or later you probably put these helpers inside package. And that's the goal of `countryscales`: Providing out-of-the-box helpers to format numbers using country-specific style conventions. ```{r, include = FALSE} x <- 12345689.987654321 ``` As a first example consider formatting a number according to style conventions used in Germany and several other European countries where a dot (`.`) is used as the big mark or grouping mark or thousands separator and a comma (`,`) as the decimal mark. Using `scales::label_number` this requires switching the default decimal and big marks: ```{r} library(scales) label_number(big.mark = ".", decimal.mark = ",", accuracy = .1)(x) ``` Using `countryscales` this could be achieved with less typing using `countryscales::label_number_de`: ```{r} library(countryscales) label_number_de(accuracy = .1)(x) ``` To provide an example of using `countryscales` with `ggplot2` let's first prepare a small example dataset of the top 10 countries according to population size: ```{r top-10-pop} top10_pop <- gapminder15[order(-gapminder15$pop), c("country", "pop")] top10_pop$pct <- top10_pop$pop / sum(top10_pop$pop) top10_pop <- head(top10_pop, 10) ``` As a basic example let's make a simple barchart of population size by country ```{r} library(ggplot2) p <- ggplot(top10_pop, aes(pop, reorder(country, pop))) + geom_col() + theme_minimal() + theme( panel.grid.major = element_blank(), panel.grid.minor = element_blank() ) + labs( x = NULL, y = NULL, title = "Top 10 of countries by population size in 2015", caption = "Data: gapminder.org" ) ``` While this chart is fine it's not ready for publication. Let's say we want to display population sizes on the x axis in decimal format and additionally add the share of each country on World population formatted as percentages as labels to the bars using German style conventions. Using `scales` this could be achieved like so: ```{r style-scales} lbl_pct <- label_percent(big.mark = ".", decimal.mark = ",", accuracy = .1) lbl_num <- label_number(big.mark = ".", decimal.mark = ",") p + # Add percentages to bars using German style conventions geom_text( aes(label = lbl_pct(pct)), hjust = 1.1, size = 8 / .pt, color = "white") + # Format numbers as percentages scale_x_continuous( labels = lbl_num, expand = c(0, 0, .05, 0)) ``` As this simple example shows displaying numbers in decimal format or as percentages requires some typing, especially if you want to or have to deviate from default decimal and grouping marks. That's where `countryscales` comes in handy as using `label_percent_de` and `scale_x_number_de` the same could be achieved with less typing like so: ```{r style-countryscales} p + # Add percentages to bars using German style conventions geom_text( aes( label = label_percent_de(accuracy = .1)(pct) ), hjust = 1.1, size = 8 / .pt, color = "white" ) + # Format numbers as percentages scale_x_number_de(expand = c(0, 0, .05, 0)) ``` ## Beyond Germany Every example above uses `label_number_de()`/`scale_x_number_de()`, but that's just the tip of the iceberg. Under the hood, all of these delegate to a general-purpose locale engine, `label_number_locale()`/ `scale_x_number_locale()` (and their `_percent_`/`_currency_` counterparts), which work with any of the `r nrow(countryscales::locales)` locale codes `countryscales` supports -- run `show_locales()` to list them all. On top of that engine, `countryscales` also ships the same ready-to-use `_de()`-style convenience family for 27 countries, from Argentina to the United States (see the [reference index](https://trekonom.github.io/countryscales/reference/index.html#countries) for the full list). Getting locale-specific formatting right also involves more than just the decimal and grouping marks shown here: correct currency symbol and sign positioning (which side of the number, with or without a space) and locale-correct percent-sign placement and spacing are full of exceptions that `scales::label_number()`/`label_currency()` don't attempt to solve. `countryscales` handles these under the hood using a modified version of `label_number()` -- see the [README's Credits section](https://trekonom.github.io/countryscales/index.html#credits) for the full story.