forked from tidyverse/ggplot2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaes-calculated.r
102 lines (96 loc) · 2.75 KB
/
aes-calculated.r
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#' Calculated aesthetics
#'
#' Most aesthetics are mapped from variables found in the data. Sometimes,
#' however, you want to map from variables computed by the aesthetic. The
#' most common example of this is the height of bars in [geom_histogram()]:
#' the height does not come from a variable in the underlying data, but
#' is instead mapped to the `count` computed by [stat_bin()]. The `calc()`
#' function is a flag to ggplot2 to it that you want to use
#' __calculated__ aesthetics produced by the statistic.
#'
#' This replaces the older approach of surrounding the variable name with
#' `..`.
#'
#' @export
#' @param x An aesthetic expression using variables calculated by the stat.
#' @examples
#' # Default histogram display
#' ggplot(mpg, aes(displ)) +
#' geom_histogram(aes(y = calc(count)))
#'
#' # Scale tallest bin to 1
#' ggplot(mpg, aes(displ)) +
#' geom_histogram(aes(y = calc(count / max(count))))
calc <- function(x) {
x
}
# Regex to determine if an identifier refers to a calculated aesthetic
match_calculated_aes <- "^\\.\\.([a-zA-Z._]+)\\.\\.$"
is_dotted_var <- function(x) {
grepl(match_calculated_aes, x)
}
# Determine if aesthetic is calculated
is_calculated_aes <- function(aesthetics) {
vapply(aesthetics, is_calculated, logical(1), USE.NAMES = FALSE)
}
is_calculated <- function(x) {
if (is.atomic(x)) {
FALSE
} else if (is.symbol(x)) {
is_dotted_var(as.character(x))
} else if (is.call(x)) {
if (identical(x[[1]], quote(calc))) {
TRUE
} else {
any(vapply(x, is_calculated, logical(1)))
}
} else if (is.pairlist(x)) {
FALSE
} else {
stop("Unknown input:", class(x)[1])
}
}
# Strip dots from expressions
strip_dots <- function(expr) {
if (is.atomic(expr)) {
expr
} else if (is.name(expr)) {
expr_ch <- as.character(expr)
if (nchar(expr_ch) > 0) {
as.name(gsub(match_calculated_aes, "\\1", expr_ch))
} else {
expr
}
} else if (is.call(expr)) {
if (identical(expr[[1]], quote(calc))) {
strip_dots(expr[[2]])
} else {
expr[-1] <- lapply(expr[-1], strip_dots)
expr
}
} else if (is.pairlist(expr)) {
# In the unlikely event of an anonymous function
as.pairlist(lapply(expr, strip_dots))
} else if (is.list(expr)) {
# For list of aesthetics
lapply(expr, strip_dots)
} else {
stop("Unknown input:", class(expr)[1])
}
}
# Convert aesthetic mapping into text labels
make_labels <- function(mapping) {
default_label <- function(aesthetic, mapping) {
# e.g., geom_smooth(aes(colour = "loess"))
if (is.atomic(mapping)) {
aesthetic
} else {
x <- deparse(strip_dots(mapping))
if (length(x) > 1) {
x <- paste0(x[[1]], "...")
}
x
}
}
Map(default_label, names(mapping), mapping)
}