1
- new_quantiles <- function (q = double(), tau = double()) {
2
- arg_is_probabilities(tau )
3
-
4
- vec_cast(q , double())
5
- vec_cast(tau , double())
6
- stopifnot(length(q ) == length(tau ))
7
- stopifnot(! vec_duplicate_any(tau ))
8
- if (is.unsorted(tau )) {
9
- o <- vec_order(tau )
10
- q <- q [o ]
11
- tau <- tau [o ]
1
+ new_quantiles <- function (values = double(), quantile_values = double()) {
2
+ arg_is_probabilities(quantile_values )
3
+
4
+ vec_cast(values , double())
5
+ vec_cast(quantile_values , double())
6
+ stopifnot(length(values ) == length(quantile_values ))
7
+ stopifnot(! vec_duplicate_any(quantile_values ))
8
+ if (is.unsorted(quantile_values )) {
9
+ o <- vec_order(quantile_values )
10
+ values <- values [o ]
11
+ quantile_values <- quantile_values [o ]
12
12
}
13
- if (is.unsorted(q , na.rm = TRUE )) {
14
- rlang :: abort (" `q [order(tau )]` produces unsorted quantiles." )
13
+ if (is.unsorted(values , na.rm = TRUE )) {
14
+ cli :: cli_abort (" `values [order(quantile_values )]` produces unsorted quantiles." )
15
15
}
16
16
17
- new_rcrd(list (q = q , tau = tau ),
18
- class = c(" dist_quantiles" , " dist_default" )
17
+ new_rcrd(list (values = values , quantile_values = quantile_values ),
18
+ class = c(" dist_quantiles" , " dist_default" )
19
19
)
20
20
}
21
21
22
+
22
23
# ' @export
23
24
vec_ptype_abbr.dist_quantiles <- function (x , ... ) " dist_qntls"
24
25
# ' @export
25
26
vec_ptype_full.dist_quantiles <- function (x , ... ) " dist_quantiles"
26
27
27
28
# ' @export
28
29
format.dist_quantiles <- function (x , digits = 2 , ... ) {
29
- q <- field(x , " q" )
30
- tau <- field(x , " tau" )
31
- rng <- range(tau , na.rm = TRUE )
32
- paste0(" [" , round(rng [1 ], digits ), " , " , round(rng [2 ], digits ), " ]<q-rng>" )
30
+ q <- field(x , " values" )
31
+ m <- suppressWarnings(median(x ))
32
+ paste0(" quantiles(" , round(m , digits ), " )[" , vctrs :: vec_size(q ), " ]" )
33
33
}
34
34
35
35
36
-
37
-
38
36
# ' A distribution parameterized by a set of quantiles
39
37
# '
40
- # ' @param x A vector of values
41
- # ' @param tau A vector of probabilities corresponding to `x `
38
+ # ' @param values A vector of values
39
+ # ' @param quantile_values A vector of probabilities corresponding to `values `
42
40
# '
43
41
# ' @export
44
42
# '
45
- # ' @import vctrs
46
43
# ' @examples
47
44
# ' dstn <- dist_quantiles(list(1:4, 8:11), list(c(.2, .4, .6, .8)))
48
45
# ' quantile(dstn, p = c(.1, .25, .5, .9))
49
46
# ' median(dstn)
50
47
# '
51
48
# ' # it's a bit annoying to inspect the data
52
- # ' vctrs::vec_data(vctrs::vec_data(dstn[1])[[1]])
53
- dist_quantiles <- function (x , tau ) {
54
- if (! is.list(x )) x <- list (x )
55
- if (! is.list(tau )) tau <- list (tau )
56
-
57
- x <- as_list_of(x , .ptype = double())
58
- tau <- as_list_of(tau , .ptype = double())
59
- args <- vec_recycle_common(x = x , tau = tau )
60
- qntls <- as_list_of(map2(args $ x , args $ tau , new_quantiles ))
49
+ # ' distributional::parameters(dstn[1])
50
+ # ' nested_quantiles(dstn[1])[[1]]
51
+ # '
52
+ # ' dist_quantiles(1:4, 1:4 / 5)
53
+ # ' dist_quantiles(1:4, c(1, 3, 2, 4) / 5)
54
+ dist_quantiles <- function (values , quantile_values ) {
55
+ if (! is.list(values )) values <- list (values )
56
+ if (! is.list(quantile_values )) quantile_values <- list (quantile_values )
57
+
58
+ values <- as_list_of(values , .ptype = double())
59
+ quantile_values <- as_list_of(quantile_values , .ptype = double())
60
+ args <- vec_recycle_common(values = values , quantile_values = quantile_values )
61
+ qntls <- as_list_of(map2(args $ values , args $ quantile_values , new_quantiles ))
61
62
new_vctr(qntls , class = " distribution" )
62
63
}
63
64
65
+ validate_dist_quantiles <- function (values , quantile_values ) {
66
+ map(quantile_values , arg_is_probabilities )
67
+ common_length <- vctrs :: vec_size_common( # aborts internally
68
+ values = values ,
69
+ quantile_values = quantile_values
70
+ )
71
+ length_diff <- vctrs :: list_sizes(values ) != vctrs :: list_sizes(quantile_values )
72
+ if (any(length_diff )) {
73
+ cli :: cli_abort(c(
74
+ " `values` and `quantile_values` must have common length." ,
75
+ i = " Mismatches found at position(s): {.val {which(length_diff)}}."
76
+ ))
77
+ }
78
+ tau_duplication <- map_lgl(quantile_values , vctrs :: vec_duplicate_any )
79
+ if (any(tau_duplication )) {
80
+ cli :: cli_abort(c(
81
+ " `quantile_values` must not be duplicated." ,
82
+ i = " Duplicates found at position(s): {.val {which(tau_duplication)}}."
83
+ ))
84
+ }
85
+ }
86
+
64
87
65
88
# ' Summarize a distribution with a set of quantiles
66
89
# '
67
90
# ' @param x a `distribution` vector
68
- # ' @param p a vector of probabilities at which to calculate quantiles
91
+ # ' @param probs a vector of probabilities at which to calculate quantiles
69
92
# ' @param ... additional arguments passed on to the `quantile` method
70
93
# '
71
94
# ' @return a `distribution` vector containing `dist_quantiles`
@@ -74,45 +97,45 @@ dist_quantiles <- function(x, tau) {
74
97
# ' @examples
75
98
# ' library(distributional)
76
99
# ' dstn <- dist_normal(c(10, 2), c(5, 10))
77
- # ' extrapolate_quantiles(dstn, p = c(.25, 0.5, .75))
100
+ # ' extrapolate_quantiles(dstn, probs = c(.25, 0.5, .75))
78
101
# '
79
102
# ' dstn <- dist_quantiles(list(1:4, 8:11), list(c(.2, .4, .6, .8)))
80
103
# ' # because this distribution is already quantiles, any extra quantiles are
81
104
# ' # appended
82
- # ' extrapolate_quantiles(dstn, p = c(.25, 0.5, .75))
105
+ # ' extrapolate_quantiles(dstn, probs = c(.25, 0.5, .75))
83
106
# '
84
107
# ' dstn <- c(
85
108
# ' dist_normal(c(10, 2), c(5, 10)),
86
109
# ' dist_quantiles(list(1:4, 8:11), list(c(.2, .4, .6, .8)))
87
110
# ' )
88
- # ' extrapolate_quantiles(dstn, p = c(.25, 0.5, .75))
89
- extrapolate_quantiles <- function (x , p , ... ) {
111
+ # ' extrapolate_quantiles(dstn, probs = c(.25, 0.5, .75))
112
+ extrapolate_quantiles <- function (x , probs , ... ) {
90
113
UseMethod(" extrapolate_quantiles" )
91
114
}
92
115
93
116
# ' @export
94
- extrapolate_quantiles.distribution <- function (x , p , ... ) {
95
- arg_is_probabilities(p )
96
- dstn <- lapply(vec_data(x ), extrapolate_quantiles , p = p , ... )
117
+ extrapolate_quantiles.distribution <- function (x , probs , ... ) {
118
+ arg_is_probabilities(probs )
119
+ dstn <- lapply(vec_data(x ), extrapolate_quantiles , p = probs , ... )
97
120
distributional ::: wrap_dist(dstn )
98
121
}
99
122
100
123
# ' @export
101
- extrapolate_quantiles.dist_default <- function (x , p , ... ) {
102
- q <- quantile(x , p , ... )
103
- new_quantiles(q = q , tau = p )
124
+ extrapolate_quantiles.dist_default <- function (x , probs , ... ) {
125
+ q <- quantile(x , probs , ... )
126
+ new_quantiles(values = q , quantile_values = probs )
104
127
}
105
128
106
129
# ' @export
107
- extrapolate_quantiles.dist_quantiles <- function (x , p , ... ) {
108
- q <- quantile(x , p , ... )
109
- tau <- field(x , " tau " )
110
- qvals <- field(x , " q " )
111
- new_quantiles(q = c(qvals , q ), tau = c(tau , p ))
130
+ extrapolate_quantiles.dist_quantiles <- function (x , probs , ... ) {
131
+ q <- quantile(x , probs , ... )
132
+ tau <- field(x , " quantile_values " )
133
+ qvals <- field(x , " values " )
134
+ new_quantiles(values = c(qvals , q ), quantile_values = c(tau , probs ))
112
135
}
113
136
114
137
is_dist_quantiles <- function (x ) {
115
- is_distribution(x ) && all(stats :: family(x ) == " quantiles" )
138
+ is_distribution(x ) & all(stats :: family(x ) == " quantiles" )
116
139
}
117
140
118
141
@@ -131,11 +154,11 @@ is_dist_quantiles <- function(x) {
131
154
# ' edf_nested %>% tidyr::unnest(q)
132
155
nested_quantiles <- function (x ) {
133
156
stopifnot(is_dist_quantiles(x ))
134
- distributional ::: dist_apply( x , .f = function ( z ) {
135
- tibble :: as_tibble(vec_data( z )) % > %
136
- dplyr :: mutate( dplyr :: across( tidyselect :: everything(), as.double ) ) %> %
137
- list_of( )
138
- } )
157
+ map(
158
+ x ,
159
+ ~ distributional :: parameters( .x ) %> %
160
+ tidyr :: unnest( tidyselect :: everything() )
161
+ )
139
162
}
140
163
141
164
@@ -190,14 +213,14 @@ pivot_quantiles <- function(.data, ...) {
190
213
.data <- .data %> %
191
214
tidyr :: unnest(tidyselect :: all_of(col )) %> %
192
215
tidyr :: pivot_wider(
193
- names_from = " tau " , values_from = " q " ,
216
+ names_from = " quantile_values " , values_from = " values " ,
194
217
names_prefix = paste0(col , " _" )
195
218
)
196
219
}
197
220
} else {
198
221
.data <- .data %> %
199
222
tidyr :: unnest(tidyselect :: all_of(cols )) %> %
200
- tidyr :: pivot_wider(names_from = " tau " , values_from = " q " )
223
+ tidyr :: pivot_wider(names_from = " quantile_values " , values_from = " values " )
201
224
}
202
225
.data
203
226
}
@@ -207,23 +230,31 @@ pivot_quantiles <- function(.data, ...) {
207
230
208
231
# ' @export
209
232
# ' @importFrom stats median qnorm family
210
- median.dist_quantiles <- function (x , ... , middle = c(" cubic" , " linear" )) {
233
+ median.dist_quantiles <- function (x , na.rm = FALSE , ... , middle = c(" cubic" , " linear" )) {
234
+ tau <- field(x , " quantile_values" )
235
+ qvals <- field(x , " values" )
236
+ if (0.5 %in% tau ) return (qvals [match(0.5 , tau )])
237
+ if (min(tau ) > 0.5 || max(tau ) < 0.5 || length(tau ) < 2 ) return (NA )
238
+ if (length(tau ) < 3 || min(tau ) > .25 || max(tau ) < .75 ) {
239
+ return (stats :: approx(tau , qvals , xout = 0.5 )$ y )
240
+ }
211
241
quantile(x , 0.5 , ... , middle = middle )
212
242
}
213
243
214
244
# placeholder to avoid errors, but not ideal
215
245
# ' @export
216
- mean.dist_quantiles <- function (x , ... , middle = c(" cubic" , " linear" )) {
246
+ mean.dist_quantiles <- function (x , na.rm = FALSE , ... , middle = c(" cubic" , " linear" )) {
217
247
median(x , ... , middle = middle )
218
248
}
219
249
220
250
# ' @export
221
251
# ' @importFrom stats quantile
222
252
# ' @import distributional
223
- quantile.dist_quantiles <- function (x , probs , ... ,
224
- middle = c(" cubic" , " linear" ),
225
- left_tail = c(" normal" , " exponential" ),
226
- right_tail = c(" normal" , " exponential" )) {
253
+ quantile.dist_quantiles <- function (
254
+ x , probs , ... ,
255
+ middle = c(" cubic" , " linear" ),
256
+ left_tail = c(" normal" , " exponential" ),
257
+ right_tail = c(" normal" , " exponential" )) {
227
258
arg_is_probabilities(probs )
228
259
middle <- match.arg(middle )
229
260
left_tail <- match.arg(left_tail )
@@ -233,8 +264,8 @@ quantile.dist_quantiles <- function(x, probs, ...,
233
264
234
265
235
266
quantile_extrapolate <- function (x , tau_out , middle , left_tail , right_tail ) {
236
- tau <- field(x , " tau " )
237
- qvals <- field(x , " q " )
267
+ tau <- field(x , " quantile_values " )
268
+ qvals <- field(x , " values " )
238
269
r <- range(tau , na.rm = TRUE )
239
270
qvals_out <- rep(NA , length(tau_out ))
240
271
@@ -244,7 +275,7 @@ quantile_extrapolate <- function(x, tau_out, middle, left_tail, right_tail) {
244
275
return (qvals [match(tau_out , tau )])
245
276
}
246
277
if (length(qvals ) < 3 || r [1 ] > .25 || r [2 ] < .75 ) {
247
- rlang :: warn (c(
278
+ cli :: cli_warn (c(
248
279
" Quantile extrapolation is not possible with fewer than" ,
249
280
" 3 quantiles or when the probs don't span [.25, .75]"
250
281
))
@@ -345,10 +376,10 @@ norm_tail_q <- function(p, q, target) {
345
376
# ' @method Math dist_quantiles
346
377
# ' @export
347
378
Math.dist_quantiles <- function (x , ... ) {
348
- tau <- field(x , " tau " )
349
- q <- field(x , " q " )
350
- q <- vctrs :: vec_math(.Generic , q , ... )
351
- new_quantiles(q = q , tau = tau )
379
+ quantile_values <- field(x , " quantile_values " )
380
+ values <- field(x , " values " )
381
+ values <- vctrs :: vec_math(.Generic , values , ... )
382
+ new_quantiles(values = values , quantile_values = quantile_values )
352
383
}
353
384
354
385
# ' @method Ops dist_quantiles
@@ -361,16 +392,16 @@ Ops.dist_quantiles <- function(e1, e2) {
361
392
is_dist <- c(inherits(e1 , " dist_default" ), inherits(e2 , " dist_default" ))
362
393
tau1 <- tau2 <- NULL
363
394
if (is_quantiles [1 ]) {
364
- q1 <- field(e1 , " q " )
365
- tau1 <- field(e1 , " tau " )
395
+ q1 <- field(e1 , " values " )
396
+ tau1 <- field(e1 , " quantile_values " )
366
397
}
367
398
if (is_quantiles [2 ]) {
368
- q2 <- field(e2 , " q " )
369
- tau2 <- field(e2 , " tau " )
399
+ q2 <- field(e2 , " values " )
400
+ tau2 <- field(e2 , " quantile_values " )
370
401
}
371
402
tau <- union(tau1 , tau2 )
372
403
if (all(is_dist )) {
373
- rlang :: abort (
404
+ cli :: cli_abort (
374
405
" You can't perform arithmetic between two distributions like this."
375
406
)
376
407
} else {
@@ -381,7 +412,7 @@ Ops.dist_quantiles <- function(e1, e2) {
381
412
}
382
413
}
383
414
q <- vctrs :: vec_arith(.Generic , q1 , q2 )
384
- new_quantiles(q = q , tau = tau )
415
+ new_quantiles(values = q , quantile_values = tau )
385
416
}
386
417
387
418
# ' @method is.na distribution
@@ -393,6 +424,6 @@ is.na.distribution <- function(x) {
393
424
# ' @method is.na dist_quantiles
394
425
# ' @export
395
426
is.na.dist_quantiles <- function (x ) {
396
- q <- field(x , " q " )
427
+ q <- field(x , " values " )
397
428
all(is.na(q ))
398
429
}
0 commit comments