I have a compositional time series containing negative values, like this:
> values <- ts(cbind(c(10,12,11,10,10),c(5,4,4,-2,2),c(2,3,4,1,1)),start=2015,frequency=2)
> values
Time Series:
Start = c(2015, 1)
End = c(2017, 1)
Frequency = 2
Series 1 Series 2 Series 3
2015.0 10 5 2
2015.5 12 4 3
2016.0 11 4 4
2016.5 10 -2 1
2017.0 10 2 1
The values are estimated components of sales due to different factors. For instance, Series 1 could be "baseline" sales, Series 2 could be a seasonal effect, Series 3 a promotional effect and so on.
Therefore, the row totals (total sales) are meaningful and important.
Now, I'd like to visualize these data. If all values were nonnegative, I'd simply use a series of stacked bar charts. Unfortunately, this doesn't work out of the box here - R puts in a "negative" rectangle that overlays and obscures one or more "positive" ones:
> barplot(t(values),names.arg=time(values))
(Note how the medium gray rectangle in 2016.5 goes down from $y=10$ to $y=8$, obscuring the top of the darkest rectangle that went up to $y=10$, and then the lightest gray one goes up again from $y=8$ to $y=9$, obscuring in turn the bottom part of the medium gray one. Unless you compare the original data, there is no way to understand what's going on.)
One common way of dealing with this is to stack all positive contributions above the horizontal axis and all the negative ones below, like this. That won't be helpful here, because you can't read off the overall totals - you would need to subtract the total bar below the axis from the bar above the axis.
I have thought about splitting each original bar in two, stacking positive contributions on the left, then collecting negative ones and "hanging" them down from the "positive" stack on the right, finally indicating the totals using a standard dot-and-line plot (code at the very bottom):
I also played around with shading "negative" rectangles using density
and lwd
to make negative contributions stand out more, but that didn't seem to help.
Alternatively, one could also use waterfall charts - specifically, one such chart per time point. Unfortunately, these need a lot of horizontal space if we have many time points and/or many components.
I can't help wondering whether there is some completely different way of visualizing such compositional series, maybe not inspired by stacked barcharts at all. Is there?
R code for what I'll call a "clothesline plot" (because the "negative" rectangles hang down from the line defined by the positive contributions):
timeseries.barplot.with.negative.values <- function(values) {
# values should be a ts matrix, one row per time point
pal <- gray.colors(ncol(values),start=0.7)
# get maximum y
foo <- values
foo[values<0] <- 0
y.max <- max(rowSums(foo))
# get minimum y
y.min <- min(c(0,rowSums(values)))
plot(c(min(time(values))-1/(3*frequency(values)),
max(time(values))+1/(3*frequency(values))),
c(y.min,y.max),type="n",xlab="",ylab="")
heights <- rep(0,nrow(values))
# plot "positive" rectangles
for ( ii in 1:ncol(values) ) {
rect( xleft=time(values)-1/(3*frequency(values)),
ybottom=heights,
xright=time(values),
ytop=heights+pmax(values[,ii],0),
col=pal[ii],border=NA)
heights <- heights+pmax(values[,ii],0)
}
# plot "negative" rectangles
for ( ii in 1:ncol(values) ) {
rect( xleft=time(values),
ybottom=heights+pmin(values[,ii],0),
xright=time(values)+1/(3*frequency(values)),
ytop=heights,
col=pal[ii],border=NA)
heights <- heights+pmin(values[,ii],0)
}
lines(heights,type="o",pch=19)
legend("topright",pch=22,pt.bg=pal,col=NA,legend=colnames(values),pt.cex=2)
}
timeseries.barplot.with.negative.values(values)