From eb3e544a98fb1946b30e46b7f0b1310cdfd2b261 Mon Sep 17 00:00:00 2001 From: David C Hall Date: Thu, 17 Mar 2022 11:17:35 -0700 Subject: [PATCH] Implement `label_log()` (#312) Fixes #311 --- NAMESPACE | 1 + NEWS.md | 3 +++ R/label-expression.R | 24 +++++++++++--------- R/label-log.R | 30 +++++++++++++++++++++++++ man/label_bytes.Rd | 1 + man/label_log.Rd | 40 +++++++++++++++++++++++++++++++++ man/label_number_si.Rd | 1 + man/label_parse.Rd | 1 + man/label_scientific.Rd | 1 + tests/testthat/test-label-log.R | 8 +++++++ 10 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 R/label-log.R create mode 100644 man/label_log.Rd create mode 100644 tests/testthat/test-label-log.R diff --git a/NAMESPACE b/NAMESPACE index 5eaaab4d..61b2fd48 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -84,6 +84,7 @@ export(label_comma) export(label_date) export(label_date_short) export(label_dollar) +export(label_log) export(label_math) export(label_number) export(label_number_auto) diff --git a/NEWS.md b/NEWS.md index e2b42ab1..73a31ee3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -39,6 +39,9 @@ * Internal `precision()`, used when `accuracy = NULL`, now avoids displaying unnecessary digits (@davidchall, #304). +* New `label_log()` displays the base and a superscript exponent, for use with + logarithmic axes (@davidchall, #312). + # scales 1.1.1 * `breaks_width()` now handles `difftime`/`hms` objects (@bhogan-mitre, #244). diff --git a/R/label-expression.R b/R/label-expression.R index fb3e54d5..bd702cce 100644 --- a/R/label-expression.R +++ b/R/label-expression.R @@ -21,18 +21,10 @@ #' # Use label_math() with continuous scales #' demo_continuous(c(1, 5)) #' demo_continuous(c(1, 5), labels = label_math(alpha[.x])) +#' demo_continuous(c(1, 5), labels = label_math()) label_parse <- function() { - # From ggplot2:::parse_safe - # See https://github.com/tidyverse/ggplot2/issues/2864 for discussion. function(text) { - text <- as.character(text) - - out <- vector("expression", length(text)) - for (i in seq_along(text)) { - expr <- parse(text = text[[i]]) - out[[i]] <- if (length(expr) == 0) NA else expr[[1]] - } - out + parse_safe(as.character(text)) } } @@ -68,3 +60,15 @@ parse_format <- label_parse #' @rdname label_parse #' @export math_format <- label_math + + +# From ggplot2:::parse_safe +# See https://github.com/tidyverse/ggplot2/issues/2864 for discussion. +parse_safe <- function(text) { + out <- vector("expression", length(text)) + for (i in seq_along(text)) { + expr <- parse(text = text[[i]]) + out[[i]] <- if (length(expr) == 0) NA else expr[[1]] + } + out +} diff --git a/R/label-log.R b/R/label-log.R new file mode 100644 index 00000000..ecd37914 --- /dev/null +++ b/R/label-log.R @@ -0,0 +1,30 @@ +#' Label numbers in log format (10^3, 10^6, etc) +#' +#' `label_log()` displays numbers as base^exponent, using superscript formatting. +#' +#' @param base Base of logarithm to use +#' @param digits Number of significant digits to show for the exponent. Argument +#' is passed on to [base::format()]. +#' @inherit number_format return +#' @seealso [breaks_log()] for the related breaks algorithm. +#' @export +#' @family labels for log scales +#' @examples +#' demo_log10(c(1, 1e5), labels = label_log()) +#' demo_log10(c(1, 1e5), breaks = breaks_log(base = 2), labels = label_log(base = 2)) +label_log <- function(base = 10, digits = 3) { + function(x) { + if (length(x) == 0) { + return(expression()) + } + + exponent <- format(log(x, base = base), digits = digits) + text <- paste0(base, "^", exponent) + ret <- parse_safe(text) + + # restore NAs from input vector + ret[is.na(x)] <- NA + + ret + } +} diff --git a/man/label_bytes.Rd b/man/label_bytes.Rd index cad8aaf9..2badca33 100644 --- a/man/label_bytes.Rd +++ b/man/label_bytes.Rd @@ -71,6 +71,7 @@ Other labels for continuous scales: \code{\link{label_scientific}()} Other labels for log scales: +\code{\link{label_log}()}, \code{\link{label_number_si}()}, \code{\link{label_scientific}()} } diff --git a/man/label_log.Rd b/man/label_log.Rd new file mode 100644 index 00000000..daf576fb --- /dev/null +++ b/man/label_log.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/label-log.R +\name{label_log} +\alias{label_log} +\title{Label numbers in log format (10^3, 10^6, etc)} +\usage{ +label_log(base = 10, digits = 3) +} +\arguments{ +\item{base}{Base of logarithm to use} + +\item{digits}{Number of significant digits to show for the exponent. Argument +is passed on to \code{\link[base:format]{base::format()}}.} +} +\value{ +All \code{label_()} functions return a "labelling" function, i.e. a function that +takes a vector \code{x} and returns a character vector of \code{length(x)} giving a +label for each input value. + +Labelling functions are designed to be used with the \code{labels} argument of +ggplot2 scales. The examples demonstrate their use with x scales, but +they work similarly for all scales, including those that generate legends +rather than axes. +} +\description{ +\code{label_log()} displays numbers as base^exponent, using superscript formatting. +} +\examples{ +demo_log10(c(1, 1e5), labels = label_log()) +demo_log10(c(1, 1e5), breaks = breaks_log(base = 2), labels = label_log(base = 2)) +} +\seealso{ +\code{\link[=breaks_log]{breaks_log()}} for the related breaks algorithm. + +Other labels for log scales: +\code{\link{label_bytes}()}, +\code{\link{label_number_si}()}, +\code{\link{label_scientific}()} +} +\concept{labels for log scales} diff --git a/man/label_number_si.Rd b/man/label_number_si.Rd index 08380d6e..98fa8004 100644 --- a/man/label_number_si.Rd +++ b/man/label_number_si.Rd @@ -62,6 +62,7 @@ Other labels for continuous scales: Other labels for log scales: \code{\link{label_bytes}()}, +\code{\link{label_log}()}, \code{\link{label_scientific}()} } \concept{labels for continuous scales} diff --git a/man/label_parse.Rd b/man/label_parse.Rd index ed1c57d2..003e1bb3 100644 --- a/man/label_parse.Rd +++ b/man/label_parse.Rd @@ -52,6 +52,7 @@ demo_discrete(greek, labels = label_parse()) # Use label_math() with continuous scales demo_continuous(c(1, 5)) demo_continuous(c(1, 5), labels = label_math(alpha[.x])) +demo_continuous(c(1, 5), labels = label_math()) } \seealso{ \link{plotmath} for the details of mathematical formatting in R. diff --git a/man/label_scientific.Rd b/man/label_scientific.Rd index b212f308..5eab6ea1 100644 --- a/man/label_scientific.Rd +++ b/man/label_scientific.Rd @@ -95,6 +95,7 @@ Other labels for continuous scales: Other labels for log scales: \code{\link{label_bytes}()}, +\code{\link{label_log}()}, \code{\link{label_number_si}()} } \concept{labels for continuous scales} diff --git a/tests/testthat/test-label-log.R b/tests/testthat/test-label-log.R new file mode 100644 index 00000000..0a555ae0 --- /dev/null +++ b/tests/testthat/test-label-log.R @@ -0,0 +1,8 @@ +test_that("label_log() returns expression", { + expect_identical(label_log()(numeric()), expression()) + expect_identical(label_log()(NA_real_), expression(NA)) + + expect_equal(label_log()(c(0.1, 10)), expression(10^-1, 10^1)) + expect_equal(label_log(base = 2)(8), expression(2^3)) + expect_equal(label_log(base = 2, digits = 3)(7), expression(2^2.81)) +})