|
| 1 | +#' Fan Plot of Distribution Percentiles Over Time |
| 2 | +#' |
| 3 | +#' Visualise sequential distributions using a range of plotting styles. |
| 4 | +#' |
| 5 | +#' @param data Set of sequential simulation data, where rows represent simulation number |
| 6 | +#' and columns represent some form of time index. If \code{data.type = "values"}, |
| 7 | +#' data must instead be a set of quantile values by rows for a set of probabilities |
| 8 | +#' (which need to be provided in \code{probs}) and by column for some form of time index. |
| 9 | +#' Data can take multiple classes, where the contents are converted to a \code{matrix}. |
| 10 | +#' If the input is a \code{mts} or \code{zoo}, the time series properties will be inherited |
| 11 | +#' (and \code{start} and \code{frequency} arguments will be ignored). |
| 12 | +#' @param data.type Indicates if \code{data} are sets of pre-calculated values for defined |
| 13 | +#' probabilities (\code{"values"}) or simulated data (\code{"simulations"}). Default is \code{"simulations"}. |
| 14 | +#' @param style Plot style, choose from \code{"fan"} (default), \code{"spaghetti"}, \code{"boxplot"} or \code{"boxfan"}. |
| 15 | +#' @param type Type of percentiles to plot in \code{fan} or \code{boxfan}. Choose from \code{"percentile"} (default) or \code{"interval"}. |
| 16 | +#' @param probs Probabilities related to percentiles or prediction intervals to be plotted |
| 17 | +#' (dependent on the \code{type} argument). Must be between 0 and 100 (inclusive) or 0 and 1. |
| 18 | +#' Percentiles greater than 50 (or 0.5), if not given, are automatically calculated as 100 - \code{p}, |
| 19 | +#' to ensure symmetric fan. Defaults to single percentile values when \code{type = "percentile"} |
| 20 | +#' and the 50th, 80th and 95th prediction interval when \code{type = "interval"}. |
| 21 | +#' @param start The time of the first distribution in \code{sims}. Similar to use in \code{\link{ts}}. |
| 22 | +#' @param frequency The number of distributions in \code{sims} per unit of time. Similar to use in \code{\link{ts}}. |
| 23 | +#' @param anchor Optional data value to anchor a forecast fan on. Typically the last observation of the observed series. |
| 24 | +#' @param anchor.time Optional time value for the anchor. Useful for irregular time series. |
| 25 | +#' @param fan.col Palette of colours used in the \code{fan} or \code{boxfan}. |
| 26 | +#' @param n.fan Number of colours to use in the fan. |
| 27 | +#' @param alpha Factor modifying the opacity alpha; typically in [0,1]. |
| 28 | +#' @param ln Vector of numbers to plot contour lines on top of \code{fan} or \code{boxfan}. |
| 29 | +#' Must correspond to calculated percentiles in \code{probs}. |
| 30 | +#' @param med.ln Logical; add a median line to fan. Useful if \code{type = "interval"}. |
| 31 | +#' @param ln.col Line colour imposed on top of the fan. Defaults to darkest colour from \code{fan.col}, |
| 32 | +#' unless \code{style = "spaghetti"}. |
| 33 | +#' @param med.col Median line colour. Defaults to first colour in \code{fan.col}. |
| 34 | +#' @param rlab Vector of labels at the end (right) of corresponding percentiles or prediction intervals. |
| 35 | +#' @param rpos Position of right labels. See \code{\link{text}}. |
| 36 | +#' @param roffset Offset of right labels. See \code{\link{text}}. |
| 37 | +#' @param rcex Text size of right labels. See \code{\link{text}}. |
| 38 | +#' @param rcol Colour of text for right labels. See \code{\link{text}}. |
| 39 | +#' @param llab Either logical (TRUE/FALSE) to plot labels at the start (left) of percentiles, |
| 40 | +#' or a vector of percentiles. Only works for \code{fan} or \code{boxfan}. |
| 41 | +#' @param lpos Position of left labels. See \code{\link{text}}. |
| 42 | +#' @param loffset Offset of left labels. Defaults to \code{roffset}. |
| 43 | +#' @param lcex Text size of left labels. Defaults to \code{rcex}. |
| 44 | +#' @param lcol Colour of text for left labels. Defaults to \code{rcol}. |
| 45 | +#' @param upplab Prefix string for upper labels when \code{type = "interval"}. |
| 46 | +#' @param lowlab Prefix string for lower labels when \code{type = "interval"}. |
| 47 | +#' @param medlab Character string for median label. |
| 48 | +#' @param n.spag Number of simulations to plot in the \code{spaghetti} style. |
| 49 | +#' @param space Space between boxes in the \code{boxfan} plot. |
| 50 | +#' @param add Logical; add to active plot. Defaults to \code{FALSE} for \code{fan}, \code{TRUE} for \code{fan0}. |
| 51 | +#' @param ylim Passed to \code{plot} when \code{add = TRUE}. |
| 52 | +#' @param ... Additional arguments passed to \code{\link{boxplot}} for \code{fan} and to \code{\link{plot}} for \code{fan0}. |
| 53 | +#' |
| 54 | +#' @details |
| 55 | +#' Sequential distribution data can be input as either simulations or pre-computed values over time (columns). |
| 56 | +#' For the latter, declare input data as percentiles by setting \code{data.type = "values"}. |
| 57 | +#' Users can choose from four styles: |
| 58 | +#' \itemize{ |
| 59 | +#' \item \code{fan}, \code{boxfan}: shaded distributions with optional contour lines and labels. |
| 60 | +#' \item \code{spaghetti}: random draws plotted along the sequence of distributions. |
| 61 | +#' \item \code{boxplot}: box plots for simulated data at appropriate locations. |
| 62 | +#' } |
| 63 | +#' |
| 64 | +#' @return See details. |
| 65 | +#' |
| 66 | +#' @references |
| 67 | +#' Abel, G. J. (2015). fanplot: An R Package for visualising sequential distributions. |
| 68 | +#' \emph{The R Journal}, 7(2), 15--23. |
| 69 | +#' |
| 70 | +#' @author Guy J. Abel |
| 71 | +#' |
| 72 | +#' @examples |
| 73 | +#' ## Basic Fan: fan0() |
| 74 | +# fan0(th.mcmc) |
| 75 | +# |
| 76 | +# ## |
| 77 | +# ## Basic Fan: fan() |
| 78 | +# ## |
| 79 | +# ### empty plot |
| 80 | +# plot(NULL, xlim = c(1, 945), ylim = range(th.mcmc)*0.85) |
| 81 | +# |
| 82 | +# # add fan |
| 83 | +# fan(th.mcmc) |
| 84 | +#' |
| 85 | +#' ## |
| 86 | +#' ## 20 or so examples of fan charts and |
| 87 | +#' ## spaghetti plots based on the th.mcmc object |
| 88 | +#' ## |
| 89 | +#' ## Make sure you have zoo, tsbugs, RColorBrewer and |
| 90 | +#' ## colorspace packages installed |
| 91 | +#' ## |
| 92 | +#' \dontrun{ |
| 93 | +#' demo("sv_fan", "fanplot") |
| 94 | +#' } |
| 95 | +#' |
| 96 | +#' ## |
| 97 | +#' ## Fans for forecasted values |
| 98 | +#' ## |
| 99 | +#' \dontrun{ |
| 100 | +#' #create time series |
| 101 | +#' net <- ts(ips$net, start=1975) |
| 102 | +#' |
| 103 | +#' # fit model |
| 104 | +#' library("forecast") |
| 105 | +#' m <- auto.arima(net) |
| 106 | +#' |
| 107 | +#' # plot in forecast package (limited customisation possible) |
| 108 | +#' plot(forecast(m, h=5)) |
| 109 | +#' |
| 110 | +#' # another plot in forecast (with some customisation, no |
| 111 | +#' # labels or anchoring possible at the moment) |
| 112 | +#' plot(forecast(m, h=5, level=c(50,80,95)), |
| 113 | +#' shadecols=rev(heat.colors(3))) |
| 114 | +#' |
| 115 | +#' # simulate future values |
| 116 | +#' mm <- matrix(NA, nrow=1000, ncol=5) |
| 117 | +#' for(i in 1:1000) |
| 118 | +#' mm[i,] <- simulate(m, nsim=5) |
| 119 | +#' |
| 120 | +#' # interval fan chart |
| 121 | +#' plot(net, xlim=c(1975,2020), ylim=c(-100,300)) |
| 122 | +#' fan(mm, type="interval", start=2013) |
| 123 | +#' |
| 124 | +#' # anchor fan chart |
| 125 | +#' plot(net, xlim=c(1975,2020), ylim=c(-100,300)) |
| 126 | +#' fan(mm, type="interval", start=2013, |
| 127 | +#' anchor=net[time(net)==2012]) |
| 128 | +#' |
| 129 | +#' # anchor spaghetti plot with underlying fan chart |
| 130 | +#' plot(net, xlim=c(1975,2020), ylim=c(-100,300)) |
| 131 | +#' fan(mm, type="interval", start=2013, |
| 132 | +#' anchor=net[time(net)==2012], alpha=0, ln.col="orange") |
| 133 | +#' fan(mm, type="interval", start=2013, |
| 134 | +#' anchor=net[time(net)==2012], alpha=0.5, style="spaghetti") |
| 135 | +#' } |
| 136 | +#' |
| 137 | +#' ## |
| 138 | +#' ## Box Plots |
| 139 | +#' ## |
| 140 | +#' # sample every 21st day of theta_t |
| 141 | +#' th.mcmc21 <- th.mcmc[, seq(1, 945, 21)] |
| 142 | +#' plot(NULL, xlim = c(1, 945), ylim = range(th.mcmc21)) |
| 143 | +#' fan(th.mcmc21, style = "boxplot", frequency = 1/21) |
| 144 | +#' |
| 145 | +#' # additional arguments for boxplot |
| 146 | +#' plot(NULL, xlim = c(1, 945), ylim = range(th.mcmc21)) |
| 147 | +#' fan(th.mcmc21, style = "boxplot", frequency = 1/21, |
| 148 | +#' outline = FALSE, col = "red", notch = TRUE) |
| 149 | +#' |
| 150 | +#' ## |
| 151 | +#' ## Fan Boxes |
| 152 | +#' ## |
| 153 | +#' plot(NULL, xlim = c(1, 945), ylim = range(th.mcmc21)) |
| 154 | +#' fan(th.mcmc21, style = "boxfan", type = "interval", frequency = 1/21) |
| 155 | +#' |
| 156 | +#' # more space between boxes |
| 157 | +#' plot(NULL, xlim = c(1, 945), ylim = range(th.mcmc21)) |
| 158 | +#' fan(th.mcmc21, style = "boxfan", type = "interval", |
| 159 | +#' frequency = 1/21, space = 10) |
| 160 | +#' |
| 161 | +#' # overlay spaghetti |
| 162 | +#' fan(th.mcmc21, style = "spaghetti", |
| 163 | +#' frequency = 1/21, n.spag = 50, ln.col = "red", alpha=0.2) |
| 164 | +#' |
| 165 | +#' |
| 166 | +#' @aliases fan fan0 |
| 167 | +#' @export |
| 168 | + |
1 | 169 | fan <- |
2 | 170 | function(data = NULL, data.type="simulations", style = "fan", type = "percentile", |
3 | 171 | probs = if(type=="percentile") seq(0.01, 0.99, 0.01) else c(0.5, 0.8, 0.95), |
@@ -296,4 +464,33 @@ fan <- |
296 | 464 | graphics::box() |
297 | 465 | } |
298 | 466 |
|
299 | | - |
| 467 | +fan0 <- |
| 468 | + function(data = NULL, data.type = "simulations", style = "fan", type = "percentile", |
| 469 | + probs = if(type=="percentile") seq(0.01, 0.99, 0.01) else c(0.5, 0.8, 0.95), |
| 470 | + start = 1, frequency = 1, anchor = NULL, anchor.time=NULL, |
| 471 | + fan.col = grDevices::heat.colors, alpha = if (style == "spaghetti") 0.5 else 1, |
| 472 | + n.fan = NULL, |
| 473 | + ln = NULL, ln.col = if(style=="spaghetti") "gray" else NULL, |
| 474 | + med.ln = if(type=="interval") TRUE else FALSE, med.col= "orange", |
| 475 | + rlab = ln, rpos = 4, roffset = 0.1, rcex = 0.8, rcol = NULL, |
| 476 | + llab = FALSE, lpos = 2, loffset = roffset, lcex = rcex, lcol = rcol, |
| 477 | + upplab = "U", lowlab = "L", medlab=if(type == "interval") "M" else NULL, |
| 478 | + n.spag = 30, |
| 479 | + space = if(style=="boxplot") 1/frequency else 0.9/frequency, |
| 480 | + add = TRUE, ylim = range(data)*0.8,...){ |
| 481 | + if(add==TRUE) |
| 482 | + plot(data[,1], type="n", ylim=ylim, ...) |
| 483 | + fan(data = data, data.type=data.type, style = style, type = type, |
| 484 | + probs = probs, |
| 485 | + start = start, frequency = frequency, anchor = anchor, anchor.time=anchor.time, |
| 486 | + fan.col = fan.col, alpha = alpha, |
| 487 | + n.fan = n.fan, |
| 488 | + ln = ln, ln.col = ln.col, |
| 489 | + med.ln = med.ln, med.col= med.col, |
| 490 | + rlab = rlab, rpos = rpos, roffset = roffset, rcex = rcex, rcol = rcol, |
| 491 | + llab = llab, lpos = lpos, loffset = loffset, lcex = lcex, lcol = lcol, |
| 492 | + upplab = upplab, lowlab = lowlab, medlab=medlab, |
| 493 | + n.spag = n.spag, |
| 494 | + space = space, |
| 495 | + add = FALSE) |
| 496 | + } |
0 commit comments