Accessibility rules for documents (WCAG 2.1, success criterion 1.1.1) ask for a text alternative for every chart that carries information. A useful alternative text says what the chart shows, not how it is drawn. ggaltdata writes it from the data of the chart itself: every number in the text is a number that is drawn.
scores <- data.frame(
item = rep(c("Apples", "Pears", "Plums", "Figs"), 2),
area = rep(c("North", "South"), each = 4),
score = c(2.80, 3.05, 3.35, 2.95, 3.35, 3.30, 3.50, 3.25)
)
p <- ggplot(scores, aes(item, score, fill = area)) +
geom_col(position = "dodge") +
labs(title = "Ratings in two markets", x = "products", y = "average score")
pThe short alternative text, one or two sentences within 250 characters:
alt_text(p)
#> [1] "South is higher than North on all 4 products; the widest gap is Apples (+0.55)."In Italian:
alt_text(p, lang = "it")
#> [1] "South è più alto di North in tutte le 4 voci; la distanza maggiore è in Apples (+0,55)."The long description and the data table, written for a screen reader in the console:
alt_describe(p)
#> Ratings in two markets. Bar chart of average score by products, for each area
#> (North and South). South is higher than North on all 4 products; the widest gap
#> is Apples (+0.55). Values are read from the data of the chart.
#>
#> products area average score
#> Apples North 2.80
#> Pears North 3.05
#> Plums North 3.35
#> Figs North 2.95
#> Apples South 3.35
#> Pears South 3.30
#> Plums South 3.50
#> Figs South 3.25| Chart | Findings |
|---|---|
| bars and dots, one series | highest and lowest, ranking |
| bars or dots in two groups | which group is higher, on how many categories, widest gap |
| stacked bars | highest and lowest total, largest part |
| differences around zero (lollipop) | how many values are above or below zero, largest values |
| estimates with intervals | highest and lowest estimate with their intervals, whether the intervals overlap, how many exclude a reference line |
| lines over time | start and end values, direction, peak, which series ends higher |
maps (geom_sf) |
areas with the highest and lowest values, median |
Other chart types get a description of their structure and a warning
of class ggaltdata_unsupported; nothing stops a report from
rendering.
R Markdown: set the chunk option
fig.alt = alt_text(p), as in the first chunk of this
vignette.
Quarto: in the chunk,
#| fig-alt: !expr alt_text(p).
The plot object: add_alt() stores the
text with labs(alt = ), so that
ggplot2::get_alt_text() and Shiny find it. A text written
by hand is kept unless overwrite = TRUE.
p2 <- add_alt(p)
ggplot2::get_alt_text(p2)
#> [1] "South is higher than North on all 4 products; the widest gap is Apples (+0.55)."Shiny: renderPlot() has an
alt argument (Shiny 1.6.0 and later).
Saved images: save_alt() writes the
text and the data next to the image.
Word, with officer: the data table after the chart.
doc <- officer::read_docx()
doc <- as_docx_table(p, doc, caption = "Data of Figure 1")
print(doc, target = "report.docx")Markdown table of the data, for an appendix:
Data of the chart
| products | area | average score |
|---|---|---|
| Apples | North | 2.80 |
| Pears | North | 3.05 |
| Plums | North | 3.35 |
| Figs | North | 2.95 |
| Apples | South | 3.35 |
| Pears | South | 3.30 |
| Plums | South | 3.50 |
| Figs | South | 3.25 |
towns <- data.frame(
town = c("Alder", "Birch", "Cedar", "Maple"),
est = c(4.05, 3.90, 3.31, 3.82), lo = c(3.93, 3.77, 3.15, 3.70), hi = c(4.17, 4.03, 3.47, 3.94)
)
q <- ggplot(towns, aes(est, town)) +
geom_pointrange(aes(xmin = lo, xmax = hi)) +
geom_vline(xintercept = 3.85, linetype = 2) +
labs(x = "satisfaction index")
qIt does not judge which finding matters most to your argument: it states the facts that the data support, and the author decides what to keep. It does not use a language model, so it cannot invent a number, and it does not describe charts it does not recognize.