From df2a36c84bf25bdbe197d876c84843d557d2444a Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 7 Oct 2024 13:14:40 +0200 Subject: [PATCH 1/5] draft function --- R/label-glue.R | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 R/label-glue.R diff --git a/R/label-glue.R b/R/label-glue.R new file mode 100644 index 00000000..0f755296 --- /dev/null +++ b/R/label-glue.R @@ -0,0 +1,8 @@ + +label_glue <- function(pattern = "{x}", ..., .envir = caller_env()) { + args <- list2(...) + force_all(pattern, .envir) + function(x) { + inject(glue::glue_data(list(x = x), pattern, !!!args, .envir = .envir)) + } +} From 29402b4d94e0b9d7022739b627905e53daef8560 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 7 Oct 2024 13:20:20 +0200 Subject: [PATCH 2/5] add test --- tests/testthat/test-label-glue.R | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/testthat/test-label-glue.R diff --git a/tests/testthat/test-label-glue.R b/tests/testthat/test-label-glue.R new file mode 100644 index 00000000..82701e4f --- /dev/null +++ b/tests/testthat/test-label-glue.R @@ -0,0 +1,10 @@ +test_that("label_glue environments work out as intended", { + + x <- LETTERS[1:3] + y <- "foo" + + # Note `{x}` should mask the `x <- LETTERS[1:3]` above + f <- label_glue("{x} and {y}") + expect_equal(f(letters[1:3]), c("a and foo", "b and foo", "c and foo")) + +}) From 0a94bcc17a7d7b748ccd249ef1d6e740ec64b51b Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 7 Oct 2024 13:21:10 +0200 Subject: [PATCH 3/5] document --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/label-glue.R | 28 +++++++++++++++++++- man/label_bytes.Rd | 1 + man/label_currency.Rd | 1 + man/label_glue.Rd | 56 ++++++++++++++++++++++++++++++++++++++++ man/label_number_auto.Rd | 1 + man/label_number_si.Rd | 1 + man/label_ordinal.Rd | 1 + man/label_parse.Rd | 2 ++ man/label_percent.Rd | 1 + man/label_pvalue.Rd | 1 + man/label_scientific.Rd | 1 + man/label_wrap.Rd | 1 + 14 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 man/label_glue.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 38d99259..0ee8fe58 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -39,4 +39,4 @@ Config/testthat/edition: 3 Encoding: UTF-8 LazyLoad: yes Roxygen: list(markdown = TRUE, r6 = FALSE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.2 diff --git a/NAMESPACE b/NAMESPACE index 323e34ba..f0872ff3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -116,6 +116,7 @@ export(label_currency) export(label_date) export(label_date_short) export(label_dollar) +export(label_glue) export(label_log) export(label_math) export(label_number) diff --git a/R/label-glue.R b/R/label-glue.R index 0f755296..94765ca3 100644 --- a/R/label-glue.R +++ b/R/label-glue.R @@ -1,4 +1,30 @@ - +#' Interpolated labels +#' +#' Use `label_glue()` to perform string interpolation using the \pkg{glue} +#' package. Enclosed expressions will be evaluated as R code. +#' +#' @param pattern A glue string used for formatting. The `x` variable holds the +#' breaks, so that `"{x}"` (default) returns the breaks as-is. +#' @param ... Arguments passed on to [`glue::glue()`]. +#' @inheritParams glue::glue +#' +#' @return A labeller function that takes a vector of breaks and returns a +#' character vector of labels. +#' @export +#' @family labels for continuous scales +#' @family labels for discrete scales +#' +#' @examples +#' # Example variables +#' animal <- "penguin" +#' species <- c("Adelie", "Chinstrap", "Emperor", "Gentoo") +#' +#' # Typical use, note that {x} will become the breaks +#' demo_discrete(species, labels = label_glue("The {x}\n{animal}")) +#' # It adapts to the breaks that are present +#' demo_discrete(species[-3], labels = label_glue("The {x}\n{animal}")) +#' # Contrary to directly glueing species + animal, which results in mislabelling! +#' demo_discrete(species[-3], labels = glue::glue("The {species}\n{animal}")) label_glue <- function(pattern = "{x}", ..., .envir = caller_env()) { args <- list2(...) force_all(pattern, .envir) diff --git a/man/label_bytes.Rd b/man/label_bytes.Rd index 02054433..7c6d9561 100644 --- a/man/label_bytes.Rd +++ b/man/label_bytes.Rd @@ -105,6 +105,7 @@ demo_continuous(c(1, 1024^2), \seealso{ Other labels for continuous scales: \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, diff --git a/man/label_currency.Rd b/man/label_currency.Rd index a29feb17..ec8ee2e8 100644 --- a/man/label_currency.Rd +++ b/man/label_currency.Rd @@ -127,6 +127,7 @@ demo_log10(c(1, 1e12), breaks = log_breaks(5, 1e3), labels = gbp) \seealso{ Other labels for continuous scales: \code{\link{label_bytes}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, diff --git a/man/label_glue.Rd b/man/label_glue.Rd new file mode 100644 index 00000000..ed10e3f8 --- /dev/null +++ b/man/label_glue.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/label-glue.R +\name{label_glue} +\alias{label_glue} +\title{Interpolated labels} +\usage{ +label_glue(pattern = "{x}", ..., .envir = caller_env()) +} +\arguments{ +\item{pattern}{A glue string used for formatting. The \code{x} variable holds the +breaks, so that \code{"{x}"} (default) returns the breaks as-is.} + +\item{...}{Arguments passed on to \code{\link[glue:glue]{glue::glue()}}.} + +\item{.envir}{[\code{environment}: \code{parent.frame()}]\cr Environment to evaluate each expression in. Expressions are +evaluated from left to right. If \code{.x} is an environment, the expressions are +evaluated in that environment and \code{.envir} is ignored. If \code{NULL} is passed, it is equivalent to \code{\link[=emptyenv]{emptyenv()}}.} +} +\value{ +A labeller function that takes a vector of breaks and returns a +character vector of labels. +} +\description{ +Use \code{label_glue()} to perform string interpolation using the \pkg{glue} +package. Enclosed expressions will be evaluated as R code. +} +\examples{ +# Example variables +animal <- "penguin" +species <- c("Adelie", "Chinstrap", "Emperor", "Gentoo") + +# Typical use, note that {x} will become the breaks +demo_discrete(species, labels = label_glue("The {x}\n{animal}")) +# It adapts to the breaks that are present +demo_discrete(species[-3], labels = label_glue("The {x}\n{animal}")) +# Contrary to directly glueing species + animal, which results in mislabelling! +demo_discrete(species[-3], labels = glue::glue("The {species}\n{animal}")) +} +\seealso{ +Other labels for continuous scales: +\code{\link{label_bytes}()}, +\code{\link{label_currency}()}, +\code{\link{label_number_auto}()}, +\code{\link{label_number_si}()}, +\code{\link{label_ordinal}()}, +\code{\link{label_parse}()}, +\code{\link{label_percent}()}, +\code{\link{label_pvalue}()}, +\code{\link{label_scientific}()} + +Other labels for discrete scales: +\code{\link{label_parse}()}, +\code{\link{label_wrap}()} +} +\concept{labels for continuous scales} +\concept{labels for discrete scales} diff --git a/man/label_number_auto.Rd b/man/label_number_auto.Rd index a660cbec..401c72dc 100644 --- a/man/label_number_auto.Rd +++ b/man/label_number_auto.Rd @@ -30,6 +30,7 @@ demo_log10(c(1, 1e7), labels = label_number_auto()) Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, \code{\link{label_parse}()}, diff --git a/man/label_number_si.Rd b/man/label_number_si.Rd index fd8c8ece..f35d160e 100644 --- a/man/label_number_si.Rd +++ b/man/label_number_si.Rd @@ -90,6 +90,7 @@ units, \code{label_number(scale_cut = cut_si("unit"))}. Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_ordinal}()}, \code{\link{label_parse}()}, diff --git a/man/label_ordinal.Rd b/man/label_ordinal.Rd index 471c0ebd..7ee2deb7 100644 --- a/man/label_ordinal.Rd +++ b/man/label_ordinal.Rd @@ -118,6 +118,7 @@ demo_continuous(c(1, 10), Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_parse}()}, diff --git a/man/label_parse.Rd b/man/label_parse.Rd index 7dd7d29d..4d4ad9da 100644 --- a/man/label_parse.Rd +++ b/man/label_parse.Rd @@ -48,6 +48,7 @@ demo_continuous(c(1, 5), labels = label_math()) Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, @@ -56,6 +57,7 @@ Other labels for continuous scales: \code{\link{label_scientific}()} Other labels for discrete scales: +\code{\link{label_glue}()}, \code{\link{label_wrap}()} } \concept{labels for continuous scales} diff --git a/man/label_percent.Rd b/man/label_percent.Rd index a705538c..ac7c2905 100644 --- a/man/label_percent.Rd +++ b/man/label_percent.Rd @@ -106,6 +106,7 @@ demo_continuous(c(0, .01), labels = french_percent) Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, diff --git a/man/label_pvalue.Rd b/man/label_pvalue.Rd index ae9e2b83..55faf7c6 100644 --- a/man/label_pvalue.Rd +++ b/man/label_pvalue.Rd @@ -55,6 +55,7 @@ demo_continuous(c(0, 1), labels = label_pvalue(prefix = prefix)) Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, diff --git a/man/label_scientific.Rd b/man/label_scientific.Rd index 97db3367..ae22a6b9 100644 --- a/man/label_scientific.Rd +++ b/man/label_scientific.Rd @@ -55,6 +55,7 @@ demo_log10(c(1, 1e9)) Other labels for continuous scales: \code{\link{label_bytes}()}, \code{\link{label_currency}()}, +\code{\link{label_glue}()}, \code{\link{label_number_auto}()}, \code{\link{label_number_si}()}, \code{\link{label_ordinal}()}, diff --git a/man/label_wrap.Rd b/man/label_wrap.Rd index 94862292..0e160437 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_glue}()}, \code{\link{label_parse}()} } \concept{labels for discrete scales} From 317b4a427463bf0eb404643cfe4d46ff6f599b7a Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 7 Oct 2024 13:22:07 +0200 Subject: [PATCH 4/5] add news bullet --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 9646d4a5..04315d22 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # scales (development version) +* New `label_glue()` labelling function for interpolated strings (#457). + # scales 1.3.0 ## Better type support From 55959408c3ffda218fb85844541fa371e4e5b75d Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 11 Oct 2024 09:39:25 +0200 Subject: [PATCH 5/5] add option to parse --- R/label-glue.R | 11 ++++++++--- man/label_glue.Rd | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/R/label-glue.R b/R/label-glue.R index 94765ca3..156d4be9 100644 --- a/R/label-glue.R +++ b/R/label-glue.R @@ -6,6 +6,7 @@ #' @param pattern A glue string used for formatting. The `x` variable holds the #' breaks, so that `"{x}"` (default) returns the breaks as-is. #' @param ... Arguments passed on to [`glue::glue()`]. +#' @param parse Whether to return labels as expressions. #' @inheritParams glue::glue #' #' @return A labeller function that takes a vector of breaks and returns a @@ -25,10 +26,14 @@ #' demo_discrete(species[-3], labels = label_glue("The {x}\n{animal}")) #' # Contrary to directly glueing species + animal, which results in mislabelling! #' demo_discrete(species[-3], labels = glue::glue("The {species}\n{animal}")) -label_glue <- function(pattern = "{x}", ..., .envir = caller_env()) { +label_glue <- function(pattern = "{x}", ..., parse = FALSE, .envir = caller_env()) { args <- list2(...) - force_all(pattern, .envir) + force_all(pattern, parse, .envir) function(x) { - inject(glue::glue_data(list(x = x), pattern, !!!args, .envir = .envir)) + x <- inject(glue::glue_data(list(x = x), pattern, !!!args, .envir = .envir)) + if (parse) { + x <- parse_safe(x) + } + x } } diff --git a/man/label_glue.Rd b/man/label_glue.Rd index ed10e3f8..3b88d0ca 100644 --- a/man/label_glue.Rd +++ b/man/label_glue.Rd @@ -4,7 +4,7 @@ \alias{label_glue} \title{Interpolated labels} \usage{ -label_glue(pattern = "{x}", ..., .envir = caller_env()) +label_glue(pattern = "{x}", ..., parse = FALSE, .envir = caller_env()) } \arguments{ \item{pattern}{A glue string used for formatting. The \code{x} variable holds the @@ -12,6 +12,8 @@ breaks, so that \code{"{x}"} (default) returns the breaks as-is.} \item{...}{Arguments passed on to \code{\link[glue:glue]{glue::glue()}}.} +\item{parse}{Whether to return labels as expressions.} + \item{.envir}{[\code{environment}: \code{parent.frame()}]\cr Environment to evaluate each expression in. Expressions are evaluated from left to right. If \code{.x} is an environment, the expressions are evaluated in that environment and \code{.envir} is ignored. If \code{NULL} is passed, it is equivalent to \code{\link[=emptyenv]{emptyenv()}}.}