What is a horizon chart?

[This article was first published on R – Statistical Odds & Ends, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

A horizon chart is a compact version of an area chart. In the words of Jonathan Schwabish (Reference 1, page 164), it is

… an area chart that is sliced into equal horizontal intervals and collapsed down into single bands, which makes the graph more compact and similar to a heatmap…

What are horizon charts good for? Here is Schwabish again:

Horizon charts are especially useful when you are visualizing time series data that are so close in value so that the data marks in, for example, a line chart, would lie atop each other. … the purpose of the horizon chart is not necessarily to enable readers to pick out specific values, but instead to easily spot general trends and identify extreme values.

Horizon charts can be a bit difficult to interpret at first, partly because the way to create them is a bit involved. Let’s illustrate how horizon charts are created with an example using the EuStockMarkets dataset from the datasets R package. (Most of this code is derived from the documentation for the horizonplot function in the latticeExtra package.)
The dataset contains the daily closing prices of 4 European stock indices from 1991-1998.

library(latticeExtra)

str(EuStockMarkets)
# Time-Series [1:1860, 1:4] from 1991 to 1999: 1629 1614 1607 1621 1618 ...
# - attr(*, "dimnames")=List of 2
#  ..$ : NULL
#  ..$ : chr [1:4] "DAX" "SMI" "CAC" "FTSE"

Let’s make a line plot of just Germany’s DAX:

# look at just DAX
dax_ts <- EuStockMarkets[, 1]

# raw plot
plot(dax_ts, ylab = "DAX", main = "Plot of DAX (1991-1998)")

First, choose a baseline level B and some bandwidth \Delta. Color each horizontal band between B + i\Delta and B + (i+1)\Delta with a different color (i is an integer, usually between -3 and 2 inclusive). In the code below, we choose a baseline of 4,000 and a bandwidth of 1,000. Bands above 4,000 are in increasingly darker shades of blue, while bands below 4,000 are in increasingly darker shades of red.

xyplot(dax_ts,
       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, col = "black")
         panel.abline(h = 4000, lty = 2)
       },
       ylab = "DAX",
       main = "Plot of DAX (1991-1998)")

Second, flip all the bands below the baseline along the baseline, then stack all the bands on top of each other, with the darker colors right on top… and that’s a horizon chart!

horizonplot(dax_ts, colorkey = TRUE,
            origin = 4000, horizonscale = 1000,
            col.regions = c("#B41414","#E03231","#F7A99C","#9FC8DC",
                            "#468CC8","#0165B3"),
            main = "Plot of DAX: 1991-1998")

The main advantage of the horizon chart is its (vertical) compactness. The horizon chart above has dimensions 600 x 200, while the earlier time series plots had dimensions 600 x 400. For comparison, here is the line plot with dimensions 600 x 200:

The real power of horizon charts comes when we want to compare multiple time series against each other. Here is the time series plot for all 4 EU stock indices (dimensions 600 x 400):

plot(EuStockMarkets, main = "Plot of EU Stock Indices (1991-1998)")

Here’s a horizon chart for the same data with the same dimensions (600 x 400). Notice how it’s easier to see the tiny fluctuations in each time series and to compare across time series.

## 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"))

## horizon chart for EU stock indices
horizonplot(EuStockMarkets, colorkey = TRUE,
            col.regions = c("#B41414","#E03231","#F7A99C","#9FC8DC",
                            "#468CC8","#0165B3"),
            origin = 4000, horizonscale = 1000,
            main = "Plot of EU Stock Indices (1991-1998)") +
  infolayers

In the horizon chart above, we set the same baseline (4,000) and same bandwidth (1,000) across the 4 stock indices. This doesn’t make a whole lot of sense because the stock indices are on different scales. The code below generates a horizon chart with each index having its own baseline and bandwidth:

horizonplot(EuStockMarkets, colorkey = TRUE,
            col.regions = c("#B41414","#E03231","#F7A99C","#9FC8DC",
                            "#468CC8","#0165B3"),
            main = "Plot of EU Stock Indices (1991-1998)") +
  infolayers

Let’s finish off this post with an example of a horizon chart from Reference 1. We want to compare public health spending (as a percentage of GDP) over time for 10 countries. Here’s what a time series plot (line chart) looks like:

And here is the horizon chart for the same data:

I think it’s much easier to make comparisons and see trends in the horizon chart as compared to the line chart.

References:

  1. Schwabish, J. (2021). Better Data Visualizations.

To leave a comment for the author, please follow the link and comment on their blog: R – Statistical Odds & Ends.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)