Reputation: 18593
With the attached code snippet, geom_rect
extends the limit of the plot, i.e., the vertical band for year 2016 is plotted even if no data are contained in that interval.
I would like the rectangles be drawn only if they contain the data points.
I read the help for annotate
and it explicitly says that annotate
has exactly the behavior I see with geom_rect
and that I would like to avoid.
Details
Note that all position aesthetics are scaled (i.e. they will expand the limits of the plot so they are visible), but all other aesthetics are set. This means that layers created with this function will never affect the legend.
library(ggplot2)
days<-rep(Sys.Date(),100)+seq(1,100)
v<-sin(as.numeric(days))
df<-data.frame(days=c(days,days),v=c(v,cos(v)+.1),n=c(rep('a',100),rep('b',100)))
shade <- data.frame(x1=c(as.Date('2016-10-15'),as.Date('2017-11-11')),
x2=c(as.Date('2016-10-20'),as.Date('2017-11-13')),
y1=c(-Inf,-Inf), y2=c(Inf,Inf))
plot(ggplot(df, aes(x=days, y=v, colour=n)) +
geom_line() +
geom_rect(data=shade, inherit.aes = F,
aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2),
color = 'grey', alpha=0.2) +
geom_point())
Upvotes: 1
Views: 2149
Reputation: 10671
Would setting limits based on range(df$days)
work for your situation?
my_limits <- range(df$days)
ggplot(df, aes(x=days, y=v, colour=n)) +
geom_line() +
geom_rect(data=shade, inherit.aes = F,
aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2),
color = 'grey', alpha=0.2) +
geom_point() +
scale_x_date(limits = my_limits) # See scale_x_datetime in case the data has time too.
Upvotes: 2
Reputation: 5766
Just filter shade
before giving it to ggplot:
library(dplyr)
library(ggplot2)
days<-rep(Sys.Date(),100)+seq(1,100)
v<-sin(as.numeric(days))
df<-data.frame(days=c(days,days),v=c(v,cos(v)+.1),n=c(rep('a',100),rep('b',100)))
shade <- data.frame(x1=c(as.Date('2016-10-15'),as.Date('2017-11-11')),
x2=c(as.Date('2016-10-20'),as.Date('2017-11-13')),
y1=c(-Inf,-Inf), y2=c(Inf,Inf)) %>%
filter(between(x1, min(df$days), max(df$days)), between(x2, min(df$days), max(df$days)))
plot(ggplot(df, aes(x=days, y=v, colour=n)) +
geom_line() +
geom_rect(data=shade, inherit.aes = F,
aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2),
color = 'grey', alpha=0.2) +
geom_point())
Upvotes: 1
Reputation: 28309
My solution creates rectangle plot and adds it to the main plot if days
are within interval of shade
. Date manipulation is performed using lubridate
package.
# Main plot that doesn't change depending on time interval
pMain <- ggplot(df, aes(days, v, colour = n)) +
geom_line() +
geom_point()
# Time manipulation part
library(lubridate)
# Turn character string to dates
df$days <- ymd(df$days)
shade$x1 <- ymd(shade$x1)
shade$x2 <- ymd(shade$x2)
# Check which shade entries contain days
foo <- c()
for(i in nrow(shade)) {
bar <- any(df$days %within% interval(shade[i, ]$x1, shade[i, ]$x2))
if (bar) {
foo <- c(foo, i)
}
}
# If any interval contained days
# Create rectangle plot and add it to the main plot
CONTAINS <- length(foo) > 0
if (CONTAINS) {
pRect <- geom_rect(data=shade[foo, ], inherit.aes = F,
aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2),
color = 'grey', alpha=0.2)
pMain <- pMain + pRect
}
plot(pMain)
Upvotes: 1