Skip to content

Commit 5871957

Browse files
committed
Better strategy for detecting calculated aes.
Closes #935
1 parent c263e94 commit 5871957

File tree

5 files changed

+70
-22
lines changed

5 files changed

+70
-22
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ LazyData: true
4848
Collate:
4949
'aaa-.r'
5050
'aaa-constants.r'
51+
'aes-calculated.r'
5152
'aes-colour-fill-alpha.r'
5253
'aes-group-order.r'
5354
'aes-linetype-size-shape.r'

R/aes-calculated.r

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Regex to determine if an identifier refers to a calculated aesthetic
2+
match_calculated_aes <- "^\\.\\.([a-zA-z._]+)\\.\\.$"
3+
4+
# Determine if aesthetic is calculated
5+
is_calculated_aes <- function(aesthetics) {
6+
vars <- lapply(aesthetics, find_vars)
7+
8+
vapply(vars, function(x) any(grepl(match_calculated_aes, x)), logical(1))
9+
}
10+
11+
find_vars <- function(expr) {
12+
if (is.name(expr)) {
13+
as.character(expr)
14+
} else if (is.atomic(expr)) {
15+
character()
16+
} else if (is.call(expr)) {
17+
unlist(lapply(expr[-1], find_vars))
18+
} else if (is.pairlist(expr)) {
19+
# In the unlikely event of an anonymous function
20+
unlist(lapply(expr, find_vars))
21+
} else {
22+
stop("Unknown input:", class(expr)[1])
23+
}
24+
}
25+
26+
# Strip dots from expressions
27+
strip_dots <- function(aesthetics) {
28+
strings <- lapply(aesthetics, deparse)
29+
strings <- lapply(strings, gsub, pattern = .calculated_aes_regex,
30+
replacement = "\\1")
31+
lapply(strings, function(x) parse(text = x)[[1]])
32+
}
33+
34+
35+
strip_dots <- function(expr) {
36+
if (is.atomic(expr)) {
37+
expr
38+
} else if (is.name(expr)) {
39+
as.name(gsub(match_calculated_aes, "\\1", as.character(expr)))
40+
} else if (is.call(expr)) {
41+
expr[-1] <- lapply(expr[-1], strip_dots)
42+
expr
43+
} else if (is.pairlist(expr)) {
44+
# In the unlikely event of an anonymous function
45+
as.pairlist(lapply(expr, expr))
46+
} else if (is.list(expr)) {
47+
# For list of aesthetics
48+
lapply(expr, strip_dots)
49+
} else {
50+
stop("Unknown input:", class(expr)[1])
51+
}
52+
}
53+

R/labels.r

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ ggtitle <- function(label) {
6565
# Convert aesthetic mapping into text labels
6666
make_labels <- function(mapping) {
6767
remove_dots <- function(x) {
68-
gsub(.calculated_aes_regex, "\\1", x)
68+
gsub(match_calculated_aes, "\\1", x)
6969
}
7070

7171
lapply(mapping, function(x) remove_dots(deparse(x)))

R/layer.r

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -257,20 +257,3 @@ Layer <- proto(expr = {
257257
#' @keywords internal
258258
#' @export
259259
layer <- Layer$new
260-
261-
# Regex to determine if an identifier refers to a calculated aesthetic
262-
.calculated_aes_regex <- "^\\.\\.([a-zA-z._]+)\\.\\.$"
263-
264-
# Determine if aesthetic is calculated
265-
is_calculated_aes <- function(aesthetics) {
266-
stats <- rep(FALSE, length(aesthetics))
267-
grepl(.calculated_aes_regex, sapply(aesthetics, deparse))
268-
}
269-
270-
# Strip dots from expressions
271-
strip_dots <- function(aesthetics) {
272-
strings <- lapply(aesthetics, deparse)
273-
strings <- lapply(strings, gsub, pattern = .calculated_aes_regex,
274-
replacement = "\\1")
275-
lapply(strings, function(x) parse(text = x)[[1]])
276-
}

inst/tests/test-layer.r

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
context("Layer")
22

3-
test_that("Correctly decide if a variable is a calculated aesthetic", {
4-
expect_true(is_calculated_aes(aes(x=..density..)))
5-
expect_false(is_calculated_aes(aes(x=a..x..b)))
6-
expect_equal(as.character(strip_dots(aes(x=..density..))), "density")
3+
test_that("Bare name surround by .. is calculated", {
4+
expect_true(is_calculated_aes(aes(..density..)))
5+
expect_true(is_calculated_aes(aes(..DENSITY..)))
6+
expect_false(is_calculated_aes(aes(a..x..b)))
7+
})
8+
9+
test_that("Calling using variable surround by .. is calculated", {
10+
expect_true(is_calculated_aes(aes(mean(..density..))))
11+
expect_true(is_calculated_aes(aes(mean(..DENSITY..))))
12+
expect_false(is_calculated_aes(aes(x=mean(a..x..b))))
13+
})
14+
15+
test_that("strip_dots remove dots around calculated aesthetics", {
16+
expect_equal(strip_dots(aes(x=..density..))$x, quote(density))
17+
expect_equal(strip_dots(aes(mean(..density..)))$x, quote(mean(density)))
718
})

0 commit comments

Comments
 (0)