11
11
# ' * A string naming the stat. To give the stat as a string, strip the
12
12
# ' function name of the `stat_` prefix. For example, to use `stat_count()`,
13
13
# ' give the stat as `"count"`.
14
- # ' @param stat.params A list of parameters parallel to the `stats` argument.
15
- # ' Use `NULL` elements to declare no parameters.
16
- # ' @param redirect A list of mappings parallel to the `stats` argument that
17
- # ' are evaluated after the stat has been computed.
14
+ # ' * The result of [`link_stat()`] to pass parameters or mapping instructions.
18
15
# '
16
+ # ' @seealso [link_stat()]
19
17
# ' @export
20
18
# '
21
19
# ' @examples
@@ -39,8 +37,6 @@ stat_chain <- function(
39
37
position = " identity" ,
40
38
... ,
41
39
stats = " identity" ,
42
- stat.params = list (),
43
- redirect = list (),
44
40
na.rm = FALSE ,
45
41
show.legend = NA ,
46
42
inherit.aes = TRUE ) {
@@ -56,60 +52,81 @@ stat_chain <- function(
56
52
params = list2(
57
53
na.rm = na.rm ,
58
54
stats = stats ,
59
- stat.params = stat.params ,
60
- redirect = redirect ,
61
55
...
62
56
)
63
57
)
64
58
}
65
59
60
+ # ' Parameterise a statistic computation
61
+ # '
62
+ # ' This is a helper function for [`stat_chain()`] to pass parameters and declare
63
+ # ' mappings.
64
+ # '
65
+ # ' @param stat The statistical transformation to use on the data. The `stat`
66
+ # ' argument accepts the following:
67
+ # ' * A `Stat` ggproto subclass, for example `StatCount`.
68
+ # ' * A string naming the stat. To give the stat as a string, strip the
69
+ # ' function name of the `stat_` prefix. For example, for `stat_count()`, give
70
+ # ' the string `"count"`.
71
+ # ' @param ... Other arguments passed to the stat as a parameter.
72
+ # ' @param mapping Set of aesthetic mappings created by [`aes()`] to be
73
+ # ' evaluated only after the stat has been computed.
74
+ # '
75
+ # ' @seealso [stat_chain()]
76
+ # ' @returns A list bundling the stat, parameters and mapping.
77
+ # ' @export
78
+ # '
79
+ # ' @examples
80
+ # ' # See `?stat_chain`
81
+ link_stat <- function (stat , ... , mapping = aes()) {
82
+ if (inherits(stat , " linked_stat" )) {
83
+ return (stat )
84
+ }
85
+
86
+ stat <- validate_subclass(stat , " Stat" )
87
+
88
+ params <- list2(... )
89
+ extra <- setdiff(names(params ), stat $ parameters(TRUE ))
90
+ if (length(extra ) > 0 ) {
91
+ cli :: cli_warn(" Ignoring unknown parameters: {.arg {extra}}." )
92
+ params <- params [setdiff(names(params ), extra )]
93
+ }
94
+
95
+ structure(
96
+ list (stat = stat , params = params , mapping = validate_mapping(mapping )),
97
+ class = " linked_stat"
98
+ )
99
+ }
100
+
66
101
# ' @rdname ggplot2-ggproto
67
102
# ' @format NULL
68
103
# ' @usage NULL
69
104
# ' @export
70
105
StatChain <- ggproto(
71
106
" StatChain" , Stat ,
72
107
73
- extra_params = c(" na.rm" , " stats" , " stat.params " , " redirect " ),
108
+ extra_params = c(" na.rm" , " stats" ),
74
109
75
110
setup_params = function (data , params ) {
76
- params $ stats <- lapply(params $ stats , validate_subclass , subclass = " Stat" )
77
- n_stats <- length(params $ stats )
78
-
79
- params $ stat.params <- force_length(
80
- params $ stat.params , n_stats ,
81
- warn_longer = TRUE , arg = " stat.params"
82
- )
83
-
84
- params $ redirect <- force_length(
85
- params $ redirect , n_stats ,
86
- warn_longer = TRUE , arg = " redirect"
87
- )
111
+ if (inherits(params $ stats , " linked_stat" )) {
112
+ # When a single linked stat is passed outside a list, repair to list
113
+ # When using a single stat, using the appropriate `stat_*()` constructor
114
+ # is better, but we should consider programmatic use too.
115
+ params $ stats <- list (params $ stats )
116
+ }
88
117
118
+ params $ stats <- lapply(params $ stats , link_stat )
89
119
params
90
120
},
91
121
92
122
compute_layer = function (self , data , params , layout ) {
93
123
94
- n_stats <- length(params $ stats )
95
-
96
- for (i in seq_len(n_stats )) {
97
- stat <- params $ stats [[i ]]
98
- param <- params $ stat.params [[i ]]
99
-
100
- # We repeat the `layer()` duty of rejecting unknown parameters
101
- valid <- stat $ parameters(TRUE )
102
- extra_param <- setdiff(names(param ), valid )
103
- if (length(extra_param ) > 0 ) {
104
- cli :: cli_warn(" Ignoring unknown parameters: {.arg {extra_param}}." )
105
- }
106
- param <- param [intersect(names(param ), valid )]
107
- if (length(param ) < 1 ) {
108
- param <- list ()
109
- }
124
+ for (i in seq_along(params $ stats )) {
125
+ link <- params $ stats [[i ]]
126
+ stat <- link $ stat
110
127
111
128
# Repeat `Layer$compute_statistic()` duty
112
- computed_param <- stat $ setup_params(data , param )
129
+ computed_param <- stat $ setup_params(data , link $ params )
113
130
computed_param $ na.rm <- computed_param $ na.rm %|| % params $ na.rm
114
131
data <- stat $ setup_data(data , computed_param )
115
132
data <- stat $ compute_layer(data , computed_param , layout )
@@ -119,8 +136,10 @@ StatChain <- ggproto(
119
136
120
137
# Repeat `Layer$map_statistic()` duty, skipping backtransforms and such
121
138
aes <- stat $ default_aes [is_calculated_aes(stat $ default_aes )]
139
+ # TODO: ideally we'd have access to Layer$computed_mapping to properly
140
+ # not touch user-specified mappings.
122
141
aes <- aes [setdiff(names(aes ), names(data ))]
123
- aes <- compact(defaults(params $ redirect [[ i ]] , aes ))
142
+ aes <- compact(defaults(link $ mapping , aes ))
124
143
if (length(aes ) == 0 ) {
125
144
next
126
145
}
@@ -136,30 +155,3 @@ StatChain <- ggproto(
136
155
data
137
156
}
138
157
)
139
-
140
- force_length <- function (x , n = length(x ), padding = list (NULL ),
141
- warn_longer = FALSE , warn_shorter = FALSE ,
142
- arg = caller_arg(x )) {
143
- force(arg )
144
- nx <- length(x )
145
- if (nx == n ) {
146
- return (x )
147
- }
148
- n_pad <- n - nx
149
- if (n_pad > 0 ) {
150
- x <- c(x , rep(padding , length = n_pad ))
151
- if (isTRUE(warn_shorter )) {
152
- cli :: cli_warn(
153
- " Padded {.arg {arg}} with {n_pad} element{?s}."
154
- )
155
- }
156
- } else if (n_pad < 0 ) {
157
- x <- x [seq_len(n )]
158
- if (isTRUE(warn_longer )) {
159
- cli :: cli_warn(
160
- " Dropped {abs(n_pad)} excess element{?s} from {.arg {arg}}."
161
- )
162
- }
163
- }
164
- x
165
- }
0 commit comments