Using geom_rect for time series shading in R

32,153

Solution 1

Code works fine, conversion to decimal date is needed for xmin and xmax, see below, requires lubridate package.

library("lubridate")
library("ggplot2")

ggplot(a_series_df)+
  geom_line(mapping = aes_string(x = "month", y = "big")) +
  geom_rect(
    fill = "red", alpha = 0.5, 
    mapping = aes_string(x = "month", y = "big"), 
    xmin = decimal_date(as.Date(c("1924-01-01"))),
    xmax = decimal_date(as.Date(c("1928-12-31"))),
    ymin = 0,
    ymax = 2
  )

Cleaner version, shading plotted first so the line colour doesn't change.

ggplot() +
  geom_rect(data = data.frame(xmin = decimal_date(as.Date(c("1924-01-01"))),
                              xmax = decimal_date(as.Date(c("1928-12-31"))),
                              ymin = -Inf,
                              ymax = Inf),
            aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
            fill = "grey", alpha = 0.5) +
  geom_line(data = a_series_df,aes(month, big), colour = "blue") +
  theme_classic()

enter image description here

Solution 2

Its a bit easier using annotate and also note that the bounds for the rectange can be specified as shown:

ggplot(a_series_df, aes(month, big)) + 
    geom_line() +
    annotate("rect", fill = "red", alpha = 0.5, 
        xmin = 1924, xmax = 1928 + 11/12,
        ymin = -Inf, ymax = Inf) +
    xlab("time")

This would also work:

library(zoo)

z <- read.zoo(a_series_df, index = 2)
autoplot(z) + 
    annotate("rect", fill = "red", alpha = 0.5, 
        xmin = 1924, xmax = 1928 + 11/12,
        ymin = -Inf, ymax = Inf) + 
    xlab("time") +
    ylab("big")

Either one gives this:

enter image description here

Solution 3

To use geom_rect you need to define your rectangle coordinate through a data.frame:

shade = data.frame(x1=c(1918,1930), x2=c(1921,1932), y1=c(-3,-3), y2=c(4,4))

#    x1   x2 y1 y2
#1 1918 1921 -3  4
#2 1930 1932 -3  4

Then you give ggplot your data and the shade data.frame:

ggplot() + 
  geom_line(aes(x=month, y=big), color='red',data=a_series_df)+
  geom_rect(data=shade, 
            mapping=aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2), color='grey', alpha=0.2)

enter image description here

Share:
32,153

Related videos on Youtube

toksing
Author by

toksing

Updated on August 15, 2020

Comments

  • toksing
    toksing over 3 years

    I am trying to shade a certain section of a time series plot (a bit like recession shading - similarly to the graph at the bottom of this article on recession shading in excel). I have put a little, possibly clumsy, sample together to illustrate. I first create a time series, plot it with ggplot2 and then want to use geom_rect to provide the shading. But I must get something wrong in the arguments.

    a<-rnorm(300)
    a_ts<-ts(a, start=c(1910, 1), frequency=12)
    a_time<-time(a_ts)
    a_series<-ts.union(big=a_ts, month=a_time)
    a_series_df<-as.data.frame(a_series)
    ggplot(a_series)+
      geom_line(mapping=aes_string(x="month", y="big"))+
      geom_rect(
        fill="red",alpha=0.5, 
        mapping=aes_string(x="month", y="big"), 
        xmin=as.numeric(as.Date(c("1924-01-01"))),
        xmax=as.numeric(as.Date(c("1928-12-31"))),
        ymin=0,
        ymax=2
        )
    

    Note that I have also tried which also did not work.

    geom_rect(
            fill="red",alpha=0.5, 
            mapping=aes_string(x="month", y="big"), 
            aes(
               xmin=as.numeric(as.Date(c("1924-01-01"))),
               xmax=as.numeric(as.Date(c("1928-12-31"))),
               ymin=0,
               ymax=2)
            )
    

    enter image description here

  • Jthorpe
    Jthorpe over 6 years
    Note that this works because aes() was passed directly to each geom, and not to ggplot(). Wierdness ensues if you supply aes(x=Date,...) to ggplot() and use geom_rect() with any other geom...
  • zx8754
    zx8754 over 6 years
    @Jthorpe it is about order of plotting, I could put aes inside ggplot and plot lines then rect, but then lines will be behind the rect. Of course we could use alpha. Preferences.
  • Jthorpe
    Jthorpe over 6 years
    Ok to be specific, If you supply aes() to ggplot(), you'll need to supply all the variables in that call to aes() in the data passed to geom_rect() and the classes of the fields will have to be consistent. Much easier to move the call to aes() to the individual geoms than construct the required variables with the right formats in the data frame passed to geom_rect().
  • Urvah Shabbir
    Urvah Shabbir almost 6 years
    does annotate("rect",...) takes in border parameter? I can not find the documentation for this. I am trying to get only a red rectangle with only the border, no fill.
  • G. Grothendieck
    G. Grothendieck almost 6 years
    @urwaCFC, Use col= instead of fill= .
  • Urvah Shabbir
    Urvah Shabbir almost 6 years
    I ended up using 4 annotate("segment",...). Still thanks, Ill try it out for future usage.
  • Rich Pauloo
    Rich Pauloo almost 4 years
    This answer has the added benefit of working with unusual geoms that don't have an explicit x and y mapping (e.g., geom_errorbar()).
  • G. Grothendieck
    G. Grothendieck about 3 years
    @Urvah, If the reason you used multiple annotates was to get multiple shaded rectangles then note that xmin and ymin can be vectors allowing a single annotate statement to be used. e.g. library(ggplot2) ggplot(a_series_df, aes(month, big)) + geom_line() + annotate("rect", fill = "red", alpha = 0.5, xmin = c(1915, 1924), xmax = c(1920, 1928) + 11/12, ymin = -Inf, ymax = Inf) + xlab("time")