Skip to content

Commit 1835fbb

Browse files
authored
Merge pull request #154 from mcol/fix_152
Add prob_outer option to ppc_ribbon() and ppc_intervals()
2 parents 6537965 + 7e6b759 commit 1835fbb

File tree

8 files changed

+143
-70
lines changed

8 files changed

+143
-70
lines changed

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ now has an argument `grid_args` to provide a way of passing optional arguments t
2727
gains an argument `discrete`, which is `FALSE` by default, but can be used to make the
2828
Geom more appropriate for discrete data. (#145)
2929

30+
* [PPC intervals plots](http://mc-stan.org/bayesplot/reference/PPC-intervals.html)
31+
and [LOO predictive checks](http://mc-stan.org/bayesplot/reference/PPC-loo.html)
32+
now draw both an outer and an inner probability interval, which can be
33+
controlled through the new argument `prob_outer` and the already existing
34+
`prob`. This is consistent with what is produced by `mcmc_intervals()`.
35+
(#152, #154, @mcol)
36+
3037

3138
# bayesplot 1.5.0
3239

R/ppc-intervals.R

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
#' @family PPCs
88
#'
99
#' @template args-y-yrep
10+
#' @template args-prob-prob_outer
1011
#' @param x A numeric vector the same length as \code{y} to use as the x-axis
1112
#' variable. For example, \code{x} could be a predictor variable from a
1213
#' regression model, a time variable for time-series models, etc. If \code{x}
1314
#' is missing or NULL, then \code{1:length(y)} is used for the x-axis.
1415
#' @param ... Currently unused.
15-
#' @param prob A value between 0 and 1 indicating the desired probability mass
16-
#' to include in the \code{yrep} intervals. The default is 0.9.
1716
#' @param alpha,size,fatten Arguments passed to geoms. For ribbon plots
1817
#' \code{alpha} and \code{size} are passed to
1918
#' \code{\link[ggplot2]{geom_ribbon}}. For interval plots \code{size} and
@@ -109,16 +108,17 @@ NULL
109108

110109
#' @rdname PPC-intervals
111110
#' @export
112-
ppc_intervals <- function(y, yrep, x = NULL, ..., prob = 0.9, size = 1,
113-
fatten = 3) {
111+
ppc_intervals <- function(y, yrep, x = NULL, ..., prob = 0.5, prob_outer = 0.9,
112+
size = 1, fatten = 3) {
114113
check_ignored_arguments(...)
115114

116115
data <- ppc_intervals_data(
117116
y = y,
118117
yrep = yrep,
119118
x = x,
120119
group = NULL,
121-
prob = prob
120+
prob = prob,
121+
prob_outer = prob_outer
122122
)
123123

124124
.ppc_intervals(
@@ -143,7 +143,8 @@ ppc_intervals_grouped <- function(y,
143143
group,
144144
facet_args = list(),
145145
...,
146-
prob = 0.9,
146+
prob = 0.5,
147+
prob_outer = 0.9,
147148
size = 1,
148149
fatten = 3) {
149150
check_ignored_arguments(...)
@@ -157,7 +158,8 @@ ppc_intervals_grouped <- function(y,
157158
yrep = yrep,
158159
x = x,
159160
group = group,
160-
prob = prob
161+
prob = prob,
162+
prob_outer = prob_outer
161163
)
162164

163165
.ppc_intervals(
@@ -178,7 +180,8 @@ ppc_ribbon <- function(y,
178180
yrep,
179181
x = NULL,
180182
...,
181-
prob = 0.9,
183+
prob = 0.5,
184+
prob_outer = 0.9,
182185
alpha = 0.33,
183186
size = 0.25) {
184187
check_ignored_arguments(...)
@@ -210,7 +213,8 @@ ppc_ribbon_grouped <- function(y,
210213
group,
211214
facet_args = list(),
212215
...,
213-
prob = 0.9,
216+
prob = 0.5,
217+
prob_outer = 0.9,
214218
alpha = 0.33,
215219
size = 0.25) {
216220
check_ignored_arguments(...)
@@ -224,7 +228,8 @@ ppc_ribbon_grouped <- function(y,
224228
yrep = yrep,
225229
x = x,
226230
group = group,
227-
prob = prob
231+
prob = prob,
232+
prob_outer = prob_outer
228233
)
229234

230235
.ppc_intervals(
@@ -241,9 +246,11 @@ ppc_ribbon_grouped <- function(y,
241246

242247
#' @rdname PPC-intervals
243248
#' @export
244-
ppc_intervals_data <- function(y, yrep, x = NULL, group = NULL, prob = 0.9, ...) {
249+
ppc_intervals_data <- function(y, yrep, x = NULL, group = NULL,
250+
prob = 0.5, prob_outer = 0.9, ...) {
245251
check_ignored_arguments(...)
246-
.ppc_intervals_data(y = y, yrep = yrep, x = x, group = group, prob = prob)
252+
.ppc_intervals_data(y = y, yrep = yrep, x = x, group = group,
253+
prob = prob, prob_outer = prob_outer)
247254
}
248255

249256

@@ -259,9 +266,14 @@ label_x <- function(x) {
259266
if (missing(x)) "Index" else NULL
260267
}
261268

262-
.ppc_intervals_data <- function(y, yrep, x = NULL, group = NULL, prob = 0.8) {
269+
.ppc_intervals_data <- function(y, yrep, x = NULL, group = NULL,
270+
prob = 0.5, prob_outer = 0.9) {
263271
grouped <- !is.null(group)
264272
stopifnot(prob > 0 && prob < 1)
273+
stopifnot(prob_outer > 0 && prob_outer <= 1)
274+
probs <- sort(c(prob, prob_outer))
275+
prob <- probs[1]
276+
prob_outer <- probs[2]
265277

266278
y <- validate_y(y)
267279
yrep <- validate_yrep(yrep, y)
@@ -283,16 +295,19 @@ label_x <- function(x) {
283295
}
284296

285297
grouped_d <- dplyr::group_by(molten_reps, !!! group_vars)
286-
alpha <- (1 - prob) / 2
298+
alpha <- (1 - probs) / 2
287299
probs <- sort(c(alpha, 0.5, 1 - alpha))
288300

289301
val_col <- sym("value")
290302
dplyr::ungroup(dplyr::summarise(
291303
grouped_d,
292-
prob = prob,
293-
lo = quantile(!! val_col, prob = probs[1]),
294-
mid = quantile(!! val_col, prob = probs[2]),
295-
hi = quantile(!! val_col, prob = probs[3])
304+
outer_width = prob_outer,
305+
inner_width = prob,
306+
ll = quantile(!! val_col, prob = probs[1]),
307+
l = quantile(!! val_col, prob = probs[2]),
308+
m = quantile(!! val_col, prob = probs[3]),
309+
h = quantile(!! val_col, prob = probs[4]),
310+
hh = quantile(!! val_col, prob = probs[5])
296311
))
297312
}
298313

@@ -317,14 +332,19 @@ label_x <- function(x) {
317332
data = data,
318333
mapping = aes_(
319334
x = ~ x,
320-
y = ~ mid,
321-
ymin = ~ lo,
322-
ymax = ~ hi
335+
y = ~ m,
336+
ymin = ~ l,
337+
ymax = ~ h
323338
)
324339
)
325340

326341
if (style == "ribbon") {
327342
graph <- graph +
343+
geom_ribbon(
344+
aes_(color = "yrep", fill = "yrep", ymin = ~ ll, ymax = ~ hh),
345+
alpha = alpha,
346+
size = size
347+
) +
328348
geom_ribbon(
329349
aes_(color = "yrep", fill = "yrep"),
330350
alpha = alpha,
@@ -341,6 +361,13 @@ label_x <- function(x) {
341361
)
342362
} else {
343363
graph <- graph +
364+
geom_pointrange(
365+
mapping = aes_(color = "yrep", fill = "yrep", ymin = ~ ll, ymax = ~ hh),
366+
shape = 21,
367+
alpha = alpha,
368+
size = size,
369+
fatten = fatten
370+
) +
344371
geom_pointrange(
345372
mapping = aes_(color = "yrep", fill = "yrep"),
346373
shape = 21,

R/ppc-loo.R

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@
9090
#'
9191
#' # loo predictive intervals vs observations
9292
#' keep_obs <- 1:50
93-
#' ppc_loo_intervals(y, yrep, psis_object = psis1, subset = keep_obs, prob = 0.9)
93+
#' ppc_loo_intervals(y, yrep, psis_object = psis1, subset = keep_obs)
9494
#'
9595
#' color_scheme_set("gray")
9696
#' ppc_loo_intervals(y, yrep, psis_object = psis1, subset = keep_obs,
97-
#' order = "median", prob = 0.9)
97+
#' order = "median")
9898
#' }
9999
#'
100100
NULL
@@ -266,20 +266,19 @@ ppc_loo_pit <-
266266

267267
#' @rdname PPC-loo
268268
#' @export
269+
#' @template args-prob-prob_outer
269270
#' @param psis_object If using \pkg{loo} version \code{2.0.0} or greater, an
270271
#' object returned by the \code{psis} function (or by the \code{loo} function
271272
#' with argument \code{save_psis} set to \code{TRUE}).
272-
#' @param prob A value between 0 and 1 indicating the desired probability mass
273-
#' to include in the intervals. The default is 0.9.
274273
#' @param intervals For \code{ppc_loo_intervals} and \code{ppc_loo_ribbon},
275-
#' optionally a matrix of precomputed LOO predictive intervals intervals with
274+
#' optionally a matrix of precomputed LOO predictive intervals
276275
#' that can be specified instead of \code{yrep} and \code{lw} (these are both
277276
#' ignored if \code{intervals} is specified). If not specified the intervals
278277
#' are computed internally before plotting. If specified, \code{intervals}
279278
#' must be a matrix with number of rows equal to the number of data points and
280-
#' three columns in the following order: the first for the lower bound of the
281-
#' interval, the second for median (50\%), and the third for the interval
282-
#' upper bound (column names are ignored).
279+
#' five columns in the following order: lower outer interval, lower inner
280+
#' interval, median (50\%), upper inner interval and upper outer interval
281+
#' (column names are ignored).
283282
#' @param order For \code{ppc_loo_intervals}, a string indicating how to arrange
284283
#' the plotted intervals. The default (\code{"index"}) is to plot them in the
285284
#' order of the observations. The alternative (\code{"median"}) arranges them
@@ -301,7 +300,8 @@ ppc_loo_intervals <-
301300
subset = NULL,
302301
intervals = NULL,
303302
...,
304-
prob = 0.9,
303+
prob = 0.5,
304+
prob_outer = 0.9,
305305
size = 1,
306306
fatten = 3,
307307
order = c("index", "median")) {
@@ -310,11 +310,14 @@ ppc_loo_intervals <-
310310
y <- validate_y(y)
311311
order_by_median <- match.arg(order) == "median"
312312
if (!is.null(intervals)) {
313-
stopifnot(is.matrix(intervals), ncol(intervals) == 3)
313+
stopifnot(is.matrix(intervals), ncol(intervals) %in% c(3, 5))
314314
message(
315315
"'intervals' specified so ignoring ",
316316
"'yrep', 'psis_object', 'subset', if specified."
317317
)
318+
if (ncol(intervals) == 3) {
319+
intervals <- cbind(intervals[, 1], intervals, intervals[, 3])
320+
}
318321
} else {
319322
suggested_package("loo", min_version = "2.0.0")
320323
yrep <- validate_yrep(yrep, y)
@@ -324,7 +327,8 @@ ppc_loo_intervals <-
324327
yrep <- yrep[, subset, drop=FALSE]
325328
psis_object <- .psis_subset(psis_object, subset)
326329
}
327-
a <- (1 - prob) / 2
330+
probs <- sort(c(prob, prob_outer))
331+
a <- (1 - probs) / 2
328332
stopifnot(identical(dim(psis_object), dim(yrep)))
329333
intervals <- suppressWarnings(t(loo::E_loo(
330334
x = yrep,
@@ -368,17 +372,21 @@ ppc_loo_ribbon <-
368372
subset = NULL,
369373
intervals = NULL,
370374
...,
371-
prob = 0.9,
375+
prob = 0.5,
376+
prob_outer = 0.9,
372377
alpha = 0.33,
373378
size = 0.25) {
374379
check_ignored_arguments(...)
375380
y <- validate_y(y)
376381
if (!is.null(intervals)) {
377-
stopifnot(is.matrix(intervals), ncol(intervals) == 3)
382+
stopifnot(is.matrix(intervals), ncol(intervals) %in% c(3, 5))
378383
message(
379384
"'intervals' specified so ignoring ",
380385
"'yrep', 'psis_object', 'subset', if specified."
381386
)
387+
if (ncol(intervals) == 3) {
388+
intervals <- cbind(intervals[, 1], intervals, intervals[, 3])
389+
}
382390
} else {
383391
suggested_package("loo", min_version = "2.0.0")
384392
yrep <- validate_yrep(yrep, y)
@@ -388,7 +396,8 @@ ppc_loo_ribbon <-
388396
yrep <- yrep[, subset, drop=FALSE]
389397
psis_object <- .psis_subset(psis_object, subset)
390398
}
391-
a <- (1 - prob) / 2
399+
probs <- sort(c(prob, prob_outer))
400+
a <- (1 - probs) / 2
392401
stopifnot(identical(dim(psis_object), dim(yrep)))
393402
intervals <- suppressWarnings(t(loo::E_loo(
394403
x = yrep,
@@ -417,9 +426,11 @@ ppc_loo_ribbon <-
417426
y_id = seq_along(y),
418427
y_obs = y,
419428
x = x,
420-
lo = intervals[, 1],
421-
mid = intervals[, 2],
422-
hi = intervals[, 3])
429+
ll = intervals[, 1],
430+
l = intervals[, 2],
431+
m = intervals[, 3],
432+
h = intervals[, 4],
433+
hh = intervals[, 5])
423434
}
424435

425436
# subset a psis_object without breaking it

man-roxygen/args-prob-prob_outer.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#' @param prob,prob_outer Values between 0 and 1 indicating the desired
2+
#' probability mass to include in the inner and outer intervals. The defaults
3+
#' are \code{prob=0.5} and \code{prob_outer=0.9}.

man/PPC-intervals.Rd

Lines changed: 13 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)