Skip to content

Commit 737eb5c

Browse files
authored
Minor break function for log10 ticks (#452)
1 parent a7038fc commit 737eb5c

File tree

6 files changed

+158
-0
lines changed

6 files changed

+158
-0
lines changed

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export(log_trans)
142142
export(logit_trans)
143143
export(manual_pal)
144144
export(math_format)
145+
export(minor_breaks_log)
145146
export(minor_breaks_n)
146147
export(minor_breaks_width)
147148
export(modulus_trans)

R/breaks-log.R

+83
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,89 @@ breaks_log <- function(n = 5, base = 10) {
8282
#' @rdname breaks_log
8383
log_breaks <- breaks_log
8484

85+
#' Minor breaks for log-10 axes
86+
#'
87+
#' This break function is designed to mark every power, multiples of 5 and/or 1
88+
#' of that power for base 10.
89+
#'
90+
#' @param detail Any of `1`, `5` and `10` to mark multiples of
91+
#' powers, multiples of 5 of powers or just powers respectively.
92+
#' @param smallest Smallest absolute value to mark when the range includes
93+
#' negative numbers.
94+
#'
95+
#' @return A function to generate minor ticks.
96+
#' @export
97+
#'
98+
#' @examples
99+
#' # Standard usage with log10 scale
100+
#' demo_log10(c(1, 1e10), minor_breaks = minor_breaks_log())
101+
#' # Increasing detail over many powers
102+
#' demo_log10(c(1, 1e10), minor_breaks = minor_breaks_log(detail = 1))
103+
#' # Adjusting until where to draw minor breaks
104+
#' demo_continuous(
105+
#' c(-1000, 1000),
106+
#' transform = asinh_trans(),
107+
#' minor_breaks = minor_breaks_log(smallest = 1)
108+
#' )
109+
minor_breaks_log <- function(detail = NULL, smallest = NULL) {
110+
if (!is.null(detail) && (!length(detail) == 1 || !detail %in% c(1, 5, 10))) {
111+
cli::cli_abort("The {.arg detail} argument must be one of 1, 5 or 10.")
112+
}
113+
if (!is.null(smallest) &&
114+
(!length(smallest) == 1 || smallest < 1e-100 || !is.finite(smallest))) {
115+
cli::cli_abort(
116+
"The {.arg smallest} argument must be a finite, positive, non-zero number."
117+
)
118+
}
119+
force(smallest)
120+
function(x, ...) {
121+
122+
has_negatives <- any(x <= 0)
123+
124+
if (has_negatives) {
125+
large <- max(abs(x))
126+
small <- smallest %||% min(c(1, large) * 0.1)
127+
x <- sort(c(small * 10, large))
128+
}
129+
130+
start <- floor(log10(min(x))) - 1L
131+
end <- ceiling(log10(max(x))) + 1L
132+
133+
if (is.null(detail)) {
134+
i <- findInterval(abs(end - start), c(8, 15), left.open = TRUE) + 1L
135+
detail <- c(1, 5, 10)[i]
136+
}
137+
138+
ladder <- 10^seq(start, end, by = 1L)
139+
tens <- fives <- ones <- numeric()
140+
if (detail %in% c(10, 5, 1)) {
141+
tens <- ladder
142+
}
143+
if (detail %in% c(5, 1)) {
144+
fives <- 5 * ladder
145+
}
146+
if (detail == 1) {
147+
ones <- as.vector(outer(1:9, ladder))
148+
ones <- setdiff(ones, c(tens, fives))
149+
}
150+
151+
if (has_negatives) {
152+
tens <- tens[tens >= small]
153+
tens <- c(tens, -tens, 0)
154+
fives <- fives[fives >= small]
155+
fives <- c(fives, -fives)
156+
ones <- ones[ones >= small]
157+
ones <- c(ones, -ones)
158+
}
159+
160+
ticks <- c(tens, fives, ones)
161+
n <- c(length(tens), length(fives), length(ones))
162+
163+
attr(ticks, "detail") <- rep(c(10, 5, 1), n)
164+
ticks
165+
}
166+
}
167+
85168
#' @author Thierry Onkelinx, \email{[email protected]}
86169
#' @noRd
87170
log_sub_breaks <- function(rng, n = 5, base = 10) {

_pkgdown.yml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ reference:
2727
contents:
2828
- starts_with("breaks_")
2929
- minor_breaks_width
30+
- minor_breaks_log
3031

3132
- title: "Bounds: ranges & rescaling"
3233
desc: >

man/minor_breaks_log.Rd

+34
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/breaks-log.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# minor_breaks_log rejects invalid arguments
2+
3+
Code
4+
minor_breaks_log(7)
5+
Condition
6+
Error in `minor_breaks_log()`:
7+
! The `detail` argument must be one of 1, 5 or 10.
8+
9+
---
10+
11+
Code
12+
minor_breaks_log(smallest = 0)
13+
Condition
14+
Error in `minor_breaks_log()`:
15+
! The `smallest` argument must be a finite, positive, non-zero number.
16+

tests/testthat/test-breaks-log.R

+23
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,26 @@ test_that("breaks_log with very small ranges fall back to extended_breaks", {
9090
extended_breaks(n = 5)(c(0.95, 3))
9191
))
9292
})
93+
94+
test_that("minor_breaks_log has correct amount of detail", {
95+
range <- c(1, 10)
96+
97+
test <- minor_breaks_log(detail = 1)(range)
98+
expect_true(all(1:10 %in% test))
99+
100+
test <- minor_breaks_log(detail = 5)(range)
101+
expect_false(all(1:10 %in% test))
102+
expect_true(all(c(1, 5, 10) %in% test))
103+
104+
test <- minor_breaks_log(detail = 10)(range)
105+
expect_true(all(c(1, 10) %in% test))
106+
expect_false(5 %in% test)
107+
108+
test <- minor_breaks_log(detail = 1)(c(-10, 10))
109+
expect_true(all(-10:10 %in% test))
110+
})
111+
112+
test_that("minor_breaks_log rejects invalid arguments", {
113+
expect_snapshot(minor_breaks_log(7), error = TRUE)
114+
expect_snapshot(minor_breaks_log(smallest = 0), error = TRUE)
115+
})

0 commit comments

Comments
 (0)