1

I want to create vertical bands for a line chart using ggplot2 or any other way in R.

    date <- seq.Date(from = as.Date("2015/01/01"), to = as.Date("2018/01/01"), by = 1)
    data <- runif(1097)
    
    df <- data.frame(cbind(date,data))
    df$date <- as.Date(df$date)
    
    datebreaks <- seq(as.Date("2015/01/01"), as.Date("2018/01/01"), by = "3 month")
    
    
    ggplot(df, aes(x = date, y = data)) + 
        geom_line() + ylim(-1,2) +
        scale_x_date(breaks = datebreaks)

gives this enter image description here

What I need is a chart with alternate quarters shaded.

ok1more
  • 779
  • 6
  • 15

2 Answers2

4

There's no ggplot method for this that I know of, but you can generate the bars pretty easily and use geom_rect.

library(ggplot2)
library(dplyr)
library(lubridate)

df <- data.frame(
  date = seq.Date(from = as.Date("2015/01/01"), to = as.Date("2018/01/01"), by = 1),
  data = runif(1097)
)


datebreaks <- seq(as.Date("2015/01/01"), as.Date("2018/01/01"), by = "3 month")

# Generate bars
df_bars <- data.frame(
  xmin = seq(as.Date("2015/01/01"), as.Date("2017/07/01"), by = "6 months"),
  ymin = -Inf, ymax = Inf
) %>% 
  mutate(xmax = xmin + months(3))

ggplot(df) + 
  geom_rect(data = df_bars, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
            fill = '#55555555') +
  geom_line(aes(x = date, y = data)) + 
  scale_x_date(breaks = datebreaks, expand = c(0,0),
               labels = function(x) paste0(year(x), ' Q', quarter(x))) + 
  ylim(c(-1,2))

Time series with quarter bars

EDIT: I added a line formatting the x-axis

  • 5
    Optional: use `ymin=-Inf, ymax=Inf`, no need to track the min/max manually. – r2evans Jan 06 '21 at 17:46
  • Thanks can you please add a line in the solution that formats the dates (tick labels as Q1 15). I am struggling with that. – ok1more Jan 06 '21 at 17:59
  • @Ashish Okay, I added that into the answer. In `scale_*_*()` functions, the `label=` parameter can be a function that takes the `breaks=` values as its argument. – Charlie Gallagher Jan 06 '21 at 19:54
3

1) xblocks The zoo package has the xblocks function allowing the following compact formulation:

library(zoo)

z <- read.zoo(df)
plot(z, xaxt = "n")
yq <- as.yearqtr(time(z))

# custom axis - optional - if omitted also omit xaxt arg in plot
yq.u <- unique(yq)
axis(1, as.Date(yq.u), yq.u)

xblocks(z, cycle(yq) %in% c(1, 3), col = adjustcolor("blue", 0.2))

screenshot

2) panel.xblocks Also the latticeExtra package has panel.xblocks so we can use the following lattice graphics code. Omit the scales argument if the default scale is desired.

library(latticeExtra)
library(zoo)

z <- read.zoo(df)
yq <- as.yearqtr(time(z))
yq.u <- unique(yq)

xyplot(z, scales = list(x = list(at = as.Date(yq.u), lab = yq.u))) + 
  layer(panel.lines(..., col = 1)) + 
  layer(panel.xblocks(z, cycle(yq) %in% c(1, 3), col = adjustcolor("blue", 0.2)))
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • Is there a way to have labels to be quarters as well? – ok1more Jan 06 '21 at 18:01
  • Have added that. – G. Grothendieck Jan 06 '21 at 18:17
  • is it complete ? I get this error ```Error in rect(xleft = blockStart, xright = blockEnd, ybottom = ybottom, : plot.new has not been called yet``` – ok1more Jan 06 '21 at 19:01
  • Maybe your session got messed up somehow. Exit and then start a fresh instance of R and copy and paste into R the data frame definition `df` from the question and then copy and paste in the code from the answer. I just did that and got the plot shown in the answer. – G. Grothendieck Jan 06 '21 at 19:09
  • I think the issue was I was trying to execute it line by line in a markdown document. It works fine in a normal R script or if I execute the whole chunk in Rmarkdown/notebook. – ok1more Jan 06 '21 at 19:10