SocialDataBlog’s kind reference in post Horizon plots with ggplot (not) motivated me to finish what the post started. I knew that ggplot2 would be a little more difficult to use for the purpose of a horizon plot, but I felt compelled to provide at least one example of a horizon plot for each of the major R graphing packages. I achieved a good result but the code is not as elegant or as flexible as I would like. Readers more comfortable with ggplot2, please bash, fork, and improve.
![]() |
From TimelyPortfolio |
R code in GIST (do raw for copy/paste):
#first attempt at implementing horizon plots in ggplot2 #pleased with result but code sloppy and inflexible #as always very open to improvements and forks require(ggplot2) require(reshape2) require(quantmod) require(PerformanceAnalytics) require(xtsExtra) data(edhec) origin = 0 horizonscale = 0.1 #get 12 month rolling return of edhec indexes roc <- as.xts(apply(cumprod(edhec+1),MARGIN=2,ROC,n=12,type="discrete"),order.by=index(edhec)) roc.df <- as.data.frame(cbind(index(roc),coredata(roc))) roc.melt <- melt(roc.df,id.vars=1) roc.melt[,1] <- as.Date(roc.melt[,1]) #convert back to a Date horizon.panel.ggplot <- function(df, title) { #df parameter should be in form of date (x), grouping, and a value (y) colnames(df) <- c("date","grouping","y") #get some decent colors from RColorBrewer #we will use colors on the edges so 2:4 for red and 7:9 for blue require(RColorBrewer) col.brew <- brewer.pal(name="RdBu",n=10) #get number of bands for the loop #limit to 3 so it will be much more manageable nbands = 3 #loop through nbands to add a column for each of the positive and negative bands for (i in 1:nbands) { #do positive df[,paste("ypos",i,sep="")] <- ifelse(df$y > origin, ifelse(abs(df$y) > horizonscale * i, horizonscale, ifelse(abs(df$y) - (horizonscale * (i - 1) - origin) > origin, abs(df$y) - (horizonscale * (i - 1) - origin), origin)), origin) #do negative df[,paste("yneg",i,sep="")] <- ifelse(df$y < origin, ifelse(abs(df$y) > horizonscale * i, horizonscale, ifelse(abs(df$y) - (horizonscale * (i - 1) - origin) > origin, abs(df$y) - (horizonscale * (i - 1) - origin), origin)), origin) } #melt data frame now that we have added a column for each band #this will fit ggplot2 expectations and make it much easier df.melt <- melt(df[,c(1:2,4:9)],id.vars=1:2) #name the columns for reference #try to be generic colnames(df.melt) <- c("date","grouping","band","value") #use ggplot to produce an area plot p <- ggplot(data=df.melt) + geom_area(aes(x = date, y = value, fill=band), #alpha=0.25, position="identity") + #this means not stacked scale_fill_manual(values=c("ypos1"=col.brew[7], #assign the colors to each of the bands; colors get darker as values increase "ypos2"=col.brew[8], "ypos3"=col.brew[9], "yneg1"=col.brew[4], "yneg2"=col.brew[3], "yneg3"=col.brew[2])) + ylim(origin,horizonscale) + #limit plot to origin and horizonscale facet_grid(grouping ~ .) + #do new subplot for each group theme_bw() + #this is optional, but I prefer to default opts(legend.position = "none", #remove legend strip.text.y = theme_text(),#rotate strip text to horizontal axis.text.y = theme_blank(),#remove y axis labels axis.ticks = theme_blank(), #remove tick marks axis.title.y = theme_blank(),#remove title for the y axis axis.title.x = theme_blank(),#remove title for the x axis title = title, #add a title from function parameter plot.title = theme_text(size=16, face="bold", hjust=0)) #format title return(p) } horizon.panel.ggplot(roc.melt, "EDHEC Indexes Return (Rolling 1 Year)")