Skip to content

Rename aesthetics in scales and consistently convert US to British spelling #2750

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 6 commits into from
Jul 23, 2018
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
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ export(scale_y_time)
export(sec_axis)
export(set_last_plot)
export(should_stop)
export(standardise_aes_names)
export(stat)
export(stat_bin)
export(stat_bin2d)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
feed data columns into `aes()` or into parameters of geoms or stats. However,
doing so remains discouraged (@clauswilke).

* Aesthetic names are now consistently standardised both in `aes()` and in the
`aesthetics` argument of scale functions. Also, the US spelling "color"
is now always internally converted to "colour", even when part of a longer
aesthetic name (e.g., `point_color`) (@clauswilke, #2649).

# ggplot2 3.0.0

Expand Down
39 changes: 30 additions & 9 deletions R/aes.r
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ NULL
#' properties (aesthetics) of geoms. Aesthetic mappings can be set in
#' [ggplot2()] and in individual layers.
#'
#' This function also standardises aesthetic names by performing partial
#' matching, converting color to colour, and translating old style R names to
#' ggplot names (e.g. pch to shape, cex to size).
#' This function also standardises aesthetic names by converting `color` to `colour`
#' (also in substrings, e.g. `point_color` to `point_colour`) and translating old style
#' R names to ggplot names (eg. `pch` to `shape`, `cex` to `size`).
#'
#' @section Quasiquotation:
#'
Expand Down Expand Up @@ -143,13 +143,34 @@ print.uneval <- function(x, ...) {
new_aes(NextMethod())
}

# Rename American or old-style aesthetics name
rename_aes <- function(x) {
# Convert prefixes to full names
full <- match(names(x), ggplot_global$all_aesthetics)
names(x)[!is.na(full)] <- ggplot_global$all_aesthetics[full[!is.na(full)]]
#' Standardise aesthetic names
#'
#' This function standardises aesthetic names by converting `color` to `colour`
#' (also in substrings, e.g. `point_color` to `point_colour`) and translating old style
#' R names to ggplot names (eg. `pch` to `shape`, `cex` to `size`).
#' @param x Character vector of aesthetics names, such as `c("colour", "size", "shape")`.
#' @return Character vector of standardised names.
#' @keywords internal
#' @export
standardise_aes_names <- function(x) {
# convert US to UK spelling of colour
x <- sub("color", "colour", x, fixed = TRUE)

plyr::rename(x, ggplot_global$base_to_ggplot, warn_missing = FALSE)
# convert old-style aesthetics names to ggplot version
plyr::revalue(x, ggplot_global$base_to_ggplot, warn_missing = FALSE)
}

# x is a list of aesthetic mappings, as generated by aes()
rename_aes <- function(x) {
Copy link
Member

Choose a reason for hiding this comment

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

Where does this get called from?

Copy link
Member Author

Choose a reason for hiding this comment

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

The main user is the aes() function, but it's actually called from many places:

ggplot2/R/aes.r

Lines 77 to 83 in 17b45f9

aes <- function(x, y, ...) {
exprs <- rlang::enquos(x = x, y = y, ...)
is_missing <- vapply(exprs, rlang::quo_is_missing, logical(1))
aes <- new_aes(exprs[!is_missing], env = parent.frame())
rename_aes(aes)
}

g$default_aes <- defaults(rename_aes(new), old)

args <- rename_aes(args)

mapping <- rename_aes(mapping)

args <- rename_aes(args)

ggplot2/R/layer.r

Lines 99 to 100 in 17b45f9

# Split up params between aesthetics, geom, and stat
params <- rename_aes(params)

override.aes = rename_aes(override.aes),

names(x) <- standardise_aes_names(names(x))
duplicated_names <- names(x)[duplicated(names(x))]
if (length(duplicated_names) > 0L) {
duplicated_message <- paste0(unique(duplicated_names), collapse = ", ")
warning(
"Duplicated aesthetics after name standardisation: ", duplicated_message, call. = FALSE
Copy link
Member

Choose a reason for hiding this comment

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

Should we make this an error?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll leave this to your decision. My own philosophy is it's only an error if the software cannot recover. Here, the duplicated aesthetics are simply ignored and a plot is produced:

library(ggplot2)
df <- data.frame(x = 1:10, y = 1:10)
ggplot(df, aes(x, y, shape = "*", pch = "a")) + geom_point()
#> Warning: Duplicated aesthetics after name standardisation: shape

It's not that different from this case, which doesn't even create a warning:

ggplot(df, aes(x, y, shape = "*")) + geom_point(aes(pch = "a"))

Created on 2018-07-13 by the reprex package (v0.2.0).

)
}
x
}

# Look up the scale that should be used for a given aesthetic
Expand Down
4 changes: 4 additions & 0 deletions R/scale-.r
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
rescaler = rescale, oob = censor, expand = waiver(), na.value = NA_real_,
trans = "identity", guide = "legend", position = "left", super = ScaleContinuous) {

aesthetics <- standardise_aes_names(aesthetics)

check_breaks_labels(breaks, labels)

position <- match.arg(position, c("left", "right", "top", "bottom"))
Expand Down Expand Up @@ -622,6 +624,8 @@ discrete_scale <- function(aesthetics, scale_name, palette, name = waiver(),
na.translate = TRUE, na.value = NA, drop = TRUE,
guide = "legend", position = "left", super = ScaleDiscrete) {

aesthetics <- standardise_aes_names(aesthetics)

check_breaks_labels(breaks, labels)

position <- match.arg(position, c("left", "right", "top", "bottom"))
Expand Down
6 changes: 3 additions & 3 deletions man/aes.Rd

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

49 changes: 39 additions & 10 deletions man/ggtheme.Rd

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

2 changes: 1 addition & 1 deletion man/scale_viridis.Rd

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

20 changes: 20 additions & 0 deletions man/standardise_aes_names.Rd

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

13 changes: 13 additions & 0 deletions tests/testthat/test-aes.r
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ test_that("labelling doesn't cause error if aesthetic is NULL", {
expect_null(p$labels$x)
})

test_that("aes standardises aesthetic names", {
# test a few common cases
expect_identical(aes(color = x), aes(colour = x))
expect_identical(aes(pch = x), aes(shape = x))

# US to British spelling in substrings
expect_identical(aes(point_color = x), aes(point_colour = x))
expect_identical(aes(color_point = x), aes(colour_point = x))

# warning when standardisation creates duplicates
expect_warning(aes(color = x, colour = y), "Duplicated aesthetics")
})


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

Expand Down