Plot many time series in parallel

Share:

Description

Plot many time series in parallel by cutting the y range into segments and overplotting them with color representing the magnitude and direction of deviation.

Usage

 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
horizonplot(x, data, ...)

## Default S3 method:
horizonplot(x, data = NULL, ...,
            nbands = 3L,
            horizonscale = NA,
            origin = function(y) na.omit(y)[1],
            colorkey = FALSE, legend = NULL,
            panel = panel.horizonplot,
            prepanel = prepanel.horizonplot,
            col.regions = brewer.pal(n = 2 * nbands, name = "RdYlBu"),
            strip = FALSE, strip.left = TRUE,
            par.strip.text = list(cex = 0.6),
            colorkey.digits = 3,
            layout = c(1, NA),
            groups = NULL,
            default.scales =
              list(y = list(relation = "free", axs = "i", 
                            draw = FALSE, tick.number = 2)))

panel.horizonplot(x, y, ..., border = NA,
                  nbands = 3L,
                  col.regions = brewer.pal(n = 2 * nbands, name = "RdYlBu"),
                  origin)

prepanel.horizonplot(x, y, ..., horizonscale = NA,
                     nbands = 3L,
                     origin = function(y) na.omit(y)[1])

Arguments

x, y

Argument on which argument dispatch is carried out. Typically this will be a multivariate time series. In the panel and prepanel functions, these are the data coordinates.

data

Not used (at least, not used by xyplot.ts).

...

further arguments. Arguments to xyplot as well as to the default panel function panel.horizonplot can be supplied directly to horizonplot. In typical usage, the method of xyplot called will be xyplot.ts.

nbands

Integer giving the number of discrete color bands used (for both negative and positive deviations from the origin).

horizonscale

the scale of each color segment. There are 3 positive segments and 3 negative segments. If this is a given as a number then all panels will have comparable distances, though not necessarily the same actual values (similar in concept to scales$relation = "sliced"). If NA, as it is by default, then the scale is chosen in each panel to cover the range of the data (unless overridden by ylim); see Details.

origin

the baseline y value for the first (positive) segment (i.e. the value at which red changes to blue). This can be a number, which is then fixed across all panels, or it can be a function, which is evaluated with the y values in each panel. The default is the first non-missing y value in each panel. See the Details section.

colorkey, legend

if colorkey = TRUE a suitable color scale bar is constructed using the values of origin and horizonscale. Further options can be passed to colorkey in list form, as with levelplot.

panel

function to render the graphic given the data. This is the function that actually implements the display.

prepanel

function determining range of the data rectangle from data to be used in a panel.

col.regions

color scale, with at least 6 colors. This should be a divergent color scale (typically with white as the central color).

strip, strip.left

by default strips are only drawn on the left, to save space.

par.strip.text

graphical parameters for the strip text; see xyplot. One notable argument here is lines, allowing multi-line text.

colorkey.digits

digits for rounding values in colorkey labels.

layout

Numeric vector of length 2 (or 3) specifying number of columns and rows (and pages) in the plot. The default is to have one column and as many rows as there are panels.

default.scales

sets default values of scales; leave this alone, pass scales instead.

groups

not applicable to this type of plot.

border

border color for the filled polygons, defaults to no border.

Details

This function draws time series as filled areas, with modifications to effectively visualise many time series in parallel. Data that would be drawn off the top of each panel is redrawn from the bottom of the panel in a darker color. Values below the origin are inverted and drawn in the opposite color. There are up to three shades (typically in blue) for data above the baseline and up to three shades (typically in red) for data below the baseline. See the article referenced below for an introduction to Horizon plots.

There are three different cases of using this function:

  1. horizonscale unspecified (default case): then each panel will have different scales, and the colors represent deviations from the origin up to the maximum deviation from the origin in that panel. If origin is specified then that will be constant across panels; otherwise it defaults to the initial value.

  2. horizonscale specified but origin unspecified: the origin defaults to the initial value in each panel, and colors represent deviations from it in steps of horizonscale (up to 3 steps each way).

  3. both horizonscale and origin specified: each panel will have the same scales, and colors represent fixed ranges of values.

In each of these cases the colorkey is labelled slightly differently (see examples).

Value

An object of class "trellis". The update method can be used to update components of the object and the print method (usually called by default) will plot it on an appropriate plotting device.

Warning

Note that the y scale in each panel defines the actual origin and scale used. The origin and horizonscale arguments are only used in the prepanel function to choose an appropriate y scale. The ylim argument therefore over-rides origin and horizonscale. This also implies that choices of scales$y$relation other than "free" may have unexpected effects, particularly "sliced", as these change the y limits from those requested by the prepanel function.

Author(s)

Felix Andrews felix@nfrac.org

References

Stephen Few (2008). Time on the Horizon. Visual Business Intelligence Newsletter, June/July 2008 http://www.perceptualedge.com/articles/visual_business_intelligence/time_on_the_horizon.pdf

See Also

Lattice, xyplot.ts, panel.xyarea

Examples

 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
## generate a random time series object with 12 columns
set.seed(1)
dat <- ts(matrix(cumsum(rnorm(200 * 12)), ncol = 12))
colnames(dat) <- paste("series", LETTERS[1:12])

## show simple line plot first, for reference.
xyplot(dat, scales = list(y = "same"))

## these layers show scale and origin in each panel...
infolayers <-
  layer(panel.scaleArrow(x = 0.99, digits = 1, col = "grey",
                         srt = 90, cex = 0.7)) +
  layer(lim <- current.panel.limits(),
    panel.text(lim$x[1], lim$y[1], round(lim$y[1],1), font = 2,
        cex = 0.7, adj = c(-0.5,-0.5), col = "#9FC8DC"))

## Case 1: each panel has a different origin and scale:
## ('origin' default is the first data value in each series).
horizonplot(dat, layout = c(1,12), colorkey = TRUE) +
  infolayers
 
## Case 2: fixed scale but different origin (baseline):
## (similar in concept to scales = "sliced")
horizonplot(dat, layout = c(1,12), horizonscale = 10, colorkey = TRUE) +
  infolayers

## Case 3: fixed scale and constant origin (all same scales):
horizonplot(dat, layout = c(1,12), origin = 0, horizonscale = 10, colorkey = TRUE) +
  infolayers

## same effect using ylim (but colorkey does not know limits):
horizonplot(dat, layout = c(1,12), ylim = c(0, 10), colorkey = TRUE) +
  infolayers

## same scales with full coverage of color scale:
horizonplot(dat, layout = c(1,12), origin = 0,
            scales = list(y = list(relation = "same")),
            colorkey = TRUE, colorkey.digits = 1) +
  infolayers


## use ylab rather than strip.left, for readability.
## also shade any times with missing data values.
horizonplot(dat, horizonscale = 10, colorkey = TRUE,
            layout = c(1,12), strip.left = FALSE,
            ylab = list(rev(colnames(dat)), rot = 0, cex = 0.7)) +
  layer_(panel.fill(col = "gray90"), panel.xblocks(..., col = "white"))


## illustration of the cut points used in the following plot
xyplot(EuStockMarkets, scales = list(y = "same"),
  panel = function(x, y, ...) {
    col <-
    c("#B41414","#E03231","#F7A99C","#9FC8DC","#468CC8","#0165B3")
    for (i in c(-3:-1, 2:0)) {
      if (i >= 0)
        yi <- pmax(4000, pmin(y, 4000 + 1000 * (i+1)))
      if (i < 0)
        yi <- pmin(4000, pmax(y, 4000 + 1000 * i))
      panel.xyarea(x, yi, origin = 4000,
        col = col[i+4], border = NA)
    }
    panel.lines(x, y)
    panel.abline(h = 4000, lty = 2)
  })

## compare with previous plot
horizonplot(EuStockMarkets, colorkey = TRUE,
            origin = 4000, horizonscale = 1000) +
  infolayers

## a cut-and-stack plot; use constant y scales!
horizonplot(sunspots, cut = list(n = 23, overlap = 0),
  scales = list(draw = FALSE, y = list(relation = "same")),
  origin = 100, colorkey = TRUE,
  strip.left = FALSE, layout = c(1,23)) +
layer(grid::grid.text(round(x[1]), x = 0, just = "left"))

Want to suggest features or report bugs for rdrr.io? Use the GitHub issue tracker.