The cols_units() function

Let’s analyze some pizzaplace data with dplyr and then make a gt table. Here we are separately defining new column labels with cols_label() and then defining the units (to combine to those labels) through cols_units(). The default pattern for combination is "{1, {2}“} which is acceptable here.

pizzaplace |>
  dplyr::mutate(month = lubridate::month(date, label = TRUE, abbr = TRUE)) |>
  dplyr::group_by(month) |>
  dplyr::summarize(
    n_sold = dplyr::n(),
    rev = sum(price)
  ) |>
  dplyr::mutate(chg = (rev - dplyr::lag(rev)) / dplyr::lag(rev)) |>
  dplyr::mutate(month = as.character(month)) |>
  gt(rowname_col = "month") |>
  fmt_integer(columns = n_sold) |>
  fmt_currency(columns = rev, use_subunits = FALSE) |>
  fmt_percent(columns = chg) |>
  sub_missing() |>
  cols_label(
    n_sold = "Number of Pizzas Sold",
    rev = "Revenue Generated",
    chg = "Monthly Changes in Revenue"
  ) |>
  cols_units(
    n_sold = "units month^-1",
    rev = "USD month^-1",
    chg = "% change *m*/*m*"
  ) |>
  cols_width(
    stub() ~ px(40),
    everything() ~ px(200)
  )
Number of Pizzas Sold, units month−1 Revenue Generated, USD month−1 Monthly Changes in Revenue, % change m/m
Jan 4,232 $69,793
Feb 3,961 $65,160 −6.64%
Mar 4,261 $70,397 8.04%
Apr 4,151 $68,737 −2.36%
May 4,328 $71,403 3.88%
Jun 4,107 $68,230 −4.44%
Jul 4,392 $72,558 6.34%
Aug 4,168 $68,278 −5.90%
Sep 3,890 $64,180 −6.00%
Oct 3,883 $64,028 −0.24%
Nov 4,266 $70,395 9.95%
Dec 3,935 $64,701 −8.09%

The sza dataset has a wealth of information and here we’ll generate a smaller table that contains the average solar zenith angles at noon for different months and at different northern latitudes. The column labels are numbers representing the latitudes and it’s convenient to apply units of ‘degrees north’ to each of them with cols_units(). The extra thing we wanted to do here was to ensure that the units are placed directly after the column labels, and we do that with .units_pattern = "{1{2}“}. This append the units ("{2”}) right to the column label ("{1“}).

sza |>
  dplyr::filter(tst == "1200") |>
  dplyr::select(-tst) |>
  dplyr::arrange(desc(latitude)) |>
  tidyr::pivot_wider(
    names_from = latitude,
    values_from = sza
  ) |>
  gt(rowname_col = "month") |>
  cols_units(
    everything() ~ ":degree:N",
    .units_pattern = "{1}{2}"
  ) |>
  tab_spanner(
    label = "Solar Zenith Angle",
    columns = everything()
  ) |>
  text_transform(
    fn = toupper,
    locations = cells_stub()
  ) |>
  tab_style(
    style = cell_text(align = "right"),
    locations = cells_stub()
  )
Solar Zenith Angle
50°N 40°N 30°N 20°N
JAN 73.0 63.0 53.0 43.0
FEB 67.2 57.2 47.2 37.2
MAR 57.7 47.7 37.7 27.7
APR 45.5 35.5 25.5 15.5
MAY 35.0 25.0 15.0 5.0
JUN 28.0 18.0 8.0 2.0
JUL 26.9 16.9 6.9 3.1
AUG 31.9 21.9 11.9 1.9
SEP 41.6 31.6 21.6 11.6
OCT 53.1 43.1 33.1 23.1
NOV 64.4 54.5 44.4 34.4
DEC 71.8 61.8 51.8 41.8

Taking a portion of the towny dataset, let’s use spanners to describe what’s in the columns and use only measurement units for the column labels. The columns labels that have to do with population and density information will be replaced with units defined in cols_units(). We’ll use a .units_pattern value of "{2“}, which means that only the units will be present (the "{1”}, representing the column label text, is omitted). Spanners added through several invocations of tab_spanner() will declare what the last four columns contain.

towny |>
  dplyr::select(
    name, land_area_km2,
    ends_with("2016"), ends_with("2021")
  ) |>
  dplyr::slice_max(population_2021, n = 10) |>
  gt(rowname_col = "name") |>
  tab_stubhead(label = "City") |>
  fmt_integer() |>
  cols_label(
    land_area_km2 ~ "Area, {{km^2}}",
    starts_with("population") ~ "",
    starts_with("density") ~ ""
  ) |>
  cols_units(
    starts_with("population") ~ "*ppl*",
    starts_with("density") ~ "*ppl* km^-2",
    .units_pattern = "{2}"
  ) |>
  tab_spanner(
    label = "Population",
    columns = starts_with("population"),
    gather = FALSE
  ) |>
  tab_spanner(
    label = "Density",
    columns = starts_with("density"),
    gather = FALSE
  ) |>
  tab_spanner(
    label = "2016",
    columns = ends_with("2016"),
    gather = FALSE
  ) |>
  tab_spanner(
    label = "2021",
    columns = ends_with("2021"),
    gather = FALSE
  ) |>
  tab_style(
    style = cell_text(align = "center"),
    locations = cells_column_labels(
      c(starts_with("population"), starts_with("density"))
    )
  ) |>
  cols_width(everything() ~ px(120)) |>
  opt_horizontal_padding(scale = 3)
2016
2021
City Area, km2
Population
Density
Population
Density
ppl ppl km−2 ppl ppl km−2
Toronto 631 2,731,571 4,328 2,794,356 4,428
Ottawa 2,788 934,243 335 1,017,449 365
Mississauga 293 721,599 2,465 717,961 2,453
Brampton 266 593,638 2,233 656,480 2,469
Hamilton 1,118 536,917 480 569,353 509
London 420 383,822 913 422,324 1,004
Markham 211 328,966 1,560 338,503 1,605
Vaughan 272 306,233 1,124 323,103 1,186
Kitchener 137 233,222 1,705 256,885 1,878
Windsor 146 217,188 1,487 229,660 1,573