Skip to content

Label dictionary #6077

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ggplot2 (development version)

* New argument `labs(dictionary)` to label based on variable name rather than
based on aesthetic (@teunbrand, #5178)
* Fixed bug in out-of-bounds binned breaks (@teunbrand, #6054)
* Binned guides now accept expressions as labels (@teunbrand, #6005)
* (internal) `Scale$get_labels()` format expressions as lists.
Expand Down
26 changes: 24 additions & 2 deletions R/labels.R
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ setup_plot_labels <- function(plot, layers, data) {
))
}

dict <- plot_labels$dictionary
if (length(dict) > 0) {
labels <- lapply(labels, function(x) {
dict <- dict[names(dict) %in% x]
x[match(names(dict), x)] <- dict
x
})
}

Comment on lines +87 to +95
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being called on all labels, right? So if a user has given a label name that just happens to be in the dictionary it will get translated

To me it should only apply to fallback labels, but I'm open to hearing against this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't roll back my approval but let's talk this one out before merging :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It applies to all 'derived' labels, not labels that users give verbatim.
Is this the kind of situation you mean?

devtools::load_all("~/packages/ggplot2/")
#> ℹ Loading ggplot2

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  labs(
    x = "Engine Displacement",
    dictionary = c(displ = "NO NOT THIS ONE", hwy = "new label")
  )

Created on 2024-12-02 with reprex v2.1.1

Copy link
Collaborator Author

@teunbrand teunbrand Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also labs(x = "foo", dictionary = c(foo = "bar")) will give "foo" as label for x.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was that last situation I was concerned about. As long as any explicitly given label is honoured I'm happy

defaults(plot_labels, labels)
}

Expand Down Expand Up @@ -114,6 +123,10 @@ setup_plot_labels <- function(plot, layers, data) {
#' bottom-right of the plot by default.
#' @param tag The text for the tag label which will be displayed at the
#' top-left of the plot by default.
#' @param dictionary A named character vector to serve as dictionary.
#' Automatically derived labels, such as those based on variables will
#' be matched with `names(dictionary)` and replaced by the matching
#' entry in `dictionary`.
#' @param alt,alt_insight Text used for the generation of alt-text for the plot.
#' See [get_alt_text] for examples. `alt` can also be a function that
#' takes the plot as input and returns text as output. `alt` also accepts
Expand All @@ -128,6 +141,14 @@ setup_plot_labels <- function(plot, layers, data) {
#' p + labs(colour = "Cylinders")
#' p + labs(x = "New x label")
#'
#' # Set labels by variable name instead of aesthetic
#' p + labs(dict = c(
#' disp = "Displacment", # Not in use
#' cyl = "Number of cylinders",
#' mpg = "Miles per gallon",
#' wt = "Weight (1000 lbs)"
#' ))
#'
#' # The plot title appears at the top-left, with the subtitle
#' # display in smaller text underneath it
#' p + labs(title = "New plot title")
Expand All @@ -146,11 +167,12 @@ setup_plot_labels <- function(plot, layers, data) {
#' labs(title = "title") +
#' labs(title = NULL)
labs <- function(..., title = waiver(), subtitle = waiver(), caption = waiver(),
tag = waiver(), alt = waiver(), alt_insight = waiver()) {
tag = waiver(), dictionary = waiver(), alt = waiver(),
alt_insight = waiver()) {
# .ignore_empty = "all" is needed to allow trailing commas, which is NOT a trailing comma for dots_list() as it's in ...
args <- dots_list(..., title = title, subtitle = subtitle, caption = caption,
tag = tag, alt = allow_lambda(alt), alt_insight = alt_insight,
.ignore_empty = "all")
dictionary = dictionary, .ignore_empty = "all")

is_waive <- vapply(args, is.waiver, logical(1))
args <- args[!is_waive]
Expand Down
14 changes: 14 additions & 0 deletions man/labs.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions tests/testthat/test-labels.R
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,31 @@ test_that("moving guide positions lets titles follow", {
expect_identical(labs[names(expect)], expect)
})

test_that("label dictionaries work", {

p <- ggplot(mtcars, aes(disp, mpg, shape = factor(cyl), size = drat)) +
geom_point() +
labs(dictionary = c(
disp = "Displacement",
mpg = "Miles per gallon",
`factor(cyl)` = "Number of cylinders",
drat = "Rear axle ratio"
))
p <- ggplot_build(p)

x <- p$layout$resolve_label(p$layout$panel_scales_x[[1]], p$plot$labels)
expect_equal(x$primary, "Displacement")

y <- p$layout$resolve_label(p$layout$panel_scales_y[[1]], p$plot$labels)
expect_equal(y$primary, "Miles per gallon")

shape <- p$plot$guides$get_params("shape")$title
expect_equal(shape, "Number of cylinders")

size <- p$plot$guides$get_params("size")$title
expect_equal(size, "Rear axle ratio")
})

# Visual tests ------------------------------------------------------------

test_that("tags are drawn correctly", {
Expand Down
Loading