diff --git a/NAMESPACE b/NAMESPACE index 6ce1d4b5..3c886227 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -120,6 +120,7 @@ export(label_comma) export(label_currency) export(label_date) export(label_date_short) +export(label_dictionary) export(label_dollar) export(label_glue) export(label_log) diff --git a/NEWS.md b/NEWS.md index 5b5a5a75..d2fa3e3d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,7 @@ using `get_palette()` or registered using `set_palette()` (#396). * `label_log()` has a `signed` argument for displaying negative numbers (@teunbrand, #421). +* New `label_dictionary()` for named lookup of labels (#458). # scales 1.3.0 diff --git a/R/label-dictionary.R b/R/label-dictionary.R new file mode 100644 index 00000000..15dd12e1 --- /dev/null +++ b/R/label-dictionary.R @@ -0,0 +1,54 @@ + +#' Labels from lookup tables +#' +#' Use `label_dictionary()` for looking up succinct breaks in a named character +#' vector giving complete labels. +#' +#' @param dictionary A named character vector of labels. The names are expected +#' to match the breaks, and the values become the labels. +#' @param nomatch A string to label breaks that do not match any name in +#' `dictionary`. When `NULL` (default), the breaks are not translated but are +#' kept as-is. +#' +#' @return A labeller function that takes a character vector of breaks and +#' returns a character vector of labels. +#' @export +#' @family labels for discrete scales +#' @examples +#' # Example lookup table +#' lut <- c( +#' "4" = "four wheel drive", +#' "r" = "rear wheel drive", +#' "f" = "front wheel drive" +#' ) +#' +#' # Typical usage +#' demo_discrete(c("4", "r", "f"), labels = label_dictionary(lut)) +#' # By default, extra values ('w') will remain as-is +#' demo_discrete(c("4", "r", "f", "w"), labels = label_dictionary(lut)) +#' # Alternatively, you can relabel extra values +#' demo_discrete( +#' c("4", "r", "f", "w"), +#' labels = label_dictionary(lut, nomatch = "unknown") +#' ) +label_dictionary <- function(dictionary = character(), nomatch = NULL) { + + if (!is.character(dictionary)) { + cli::cli_abort("The {.arg dictionary} argument must be a character vector.") + } + if (!is_named2(dictionary)) { + cli::cli_abort("The {.arg dictionary} argument must have names.") + } + names <- names(dictionary) + values <- unname(dictionary) + + force(nomatch) + + function(x) { + i <- match(x, names, nomatch = NA_integer_) + out <- values[i] + missing <- is.na(i) + out[missing] <- if (is.null(nomatch)) x[missing] else nomatch + out + } +} diff --git a/man/label_dictionary.Rd b/man/label_dictionary.Rd new file mode 100644 index 00000000..ded7a697 --- /dev/null +++ b/man/label_dictionary.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/label-lut.R +\name{label_dictionary} +\alias{label_dictionary} +\title{Labels from lookup tables} +\usage{ +label_dictionary(dictionary = character(), nomatch = NULL) +} +\arguments{ +\item{dictionary}{A named character vector of labels. The names are expected +to match the breaks, and the values become the labels.} + +\item{nomatch}{A string to label breaks that do not match any name in +\code{dictionary}. When \code{NULL} (default), the breaks are not translated but are +kept as-is.} +} +\value{ +A labeller function that takes a character vector of breaks and +returns a character vector of labels. +} +\description{ +Use \code{label_dictionary()} for looking up succinct breaks in a named character +vector giving complete labels. +} +\examples{ +# Example lookup table +lut <- c( + "4" = "four wheel drive", + "r" = "rear wheel drive", + "f" = "front wheel drive" +) + +# Typical usage +demo_discrete(c("4", "r", "f"), labels = label_dictionary(lut)) +# By default, extra values ('w') will remain as-is +demo_discrete(c("4", "r", "f", "w"), labels = label_dictionary(lut)) +# Alternatively, you can relabel extra values +demo_discrete( + c("4", "r", "f", "w"), + labels = label_dictionary(lut, nomatch = "unknown") +) +} +\seealso{ +Other labels for discrete scales: +\code{\link{label_glue}()}, +\code{\link{label_parse}()}, +\code{\link{label_wrap}()} +} +\concept{labels for discrete scales} diff --git a/man/label_glue.Rd b/man/label_glue.Rd index 3b88d0ca..f4001974 100644 --- a/man/label_glue.Rd +++ b/man/label_glue.Rd @@ -51,6 +51,7 @@ Other labels for continuous scales: \code{\link{label_scientific}()} Other labels for discrete scales: +\code{\link{label_dictionary}()}, \code{\link{label_parse}()}, \code{\link{label_wrap}()} } diff --git a/man/label_parse.Rd b/man/label_parse.Rd index 4d4ad9da..8c712aff 100644 --- a/man/label_parse.Rd +++ b/man/label_parse.Rd @@ -57,6 +57,7 @@ Other labels for continuous scales: \code{\link{label_scientific}()} Other labels for discrete scales: +\code{\link{label_dictionary}()}, \code{\link{label_glue}()}, \code{\link{label_wrap}()} } diff --git a/man/label_wrap.Rd b/man/label_wrap.Rd index 0e160437..f853a7bc 100644 --- a/man/label_wrap.Rd +++ b/man/label_wrap.Rd @@ -34,6 +34,7 @@ demo_discrete(x, labels = label_wrap(20)) } \seealso{ Other labels for discrete scales: +\code{\link{label_dictionary}()}, \code{\link{label_glue}()}, \code{\link{label_parse}()} } diff --git a/man/number_options.Rd b/man/number_options.Rd index d9c81aa5..781eafe6 100644 --- a/man/number_options.Rd +++ b/man/number_options.Rd @@ -40,7 +40,7 @@ The default (\code{NULL}) retrieves the setting from the \item{style_negative}{A string that determines the style of negative numbers: \itemize{ -\item \code{"hyphen"} (the default): preceded by a standard hypen \code{-}, e.g. \code{-1}. +\item \code{"hyphen"} (the default): preceded by a standard hyphen \code{-}, e.g. \code{-1}. \item \code{"minus"}, uses a proper Unicode minus symbol. This is a typographical nicety that ensures \code{-} aligns with the horizontal bar of the the horizontal bar of \code{+}. diff --git a/tests/testthat/test-label-dictionary.R b/tests/testthat/test-label-dictionary.R new file mode 100644 index 00000000..eed01331 --- /dev/null +++ b/tests/testthat/test-label-dictionary.R @@ -0,0 +1,11 @@ +test_that("label_dictionary gives correct answers", { + + short <- c("A", "B", "C") + lut <- c("A" = "Apple", "C" = "Cherry", "D" = "Date") + + expect_equal(label_dictionary(lut)(short), c("Apple", "B", "Cherry")) + expect_equal( + label_dictionary(lut, nomatch = "Banana")(short), + c("Apple", "Banana", "Cherry") + ) +})