Zeke Zeke - 2 months ago 16
R Question

Having to multiply geoms by number of facets?

I want to have multiple rectangles behind my data, but I also want to use multiple facets, every one of which these rectangles will appear in. I first ran the code below and got the error:

Aesthetics must be either length 1 or the same as the data (12): fill


Here's my code:

block_rects <- data.frame(xstart_rect=c(-0.5, 0.5, 1.5, 2.5, 3.5, 4.5),
xend_rect=c(0.5, 1.5, 2.5, 3.5, 4.5, 5.5))
df <- data.frame(xs=c(1,2,3),ys=c(1,2,3),cond=c("a","b","f"),fs=c("x","x","y"))
df %>% ggplot(aes(x=xs,y=ys,color=cond)) +
geom_rect(inherit.aes = FALSE,
data = block_rects, aes(xmin = xstart_rect, xmax = xend_rect,
ymin = -Inf, ymax = Inf),
fill = c("#f1f1f1", "white","white","white","#f1f1f1","white")) +
geom_point() +
facet_wrap(~ fs)


However, I realized that when I doubled the number of items in the
fill
vector in
geom_rect
, it worked. I worked out that I had to multiply the
fill
vector by the number of facets the plot has (e.g. if I change
fs
to
c("x","y","z")
I need to multiple the
fill
column by three).

What the heck is up with that behavior? Is this a bug in the code? If not, how should I make my code so that any number of facets can be used? I don't want to have to explicitly code which variables are being faceted in
geom_rect
.

Answer

As you figured out, you are drawing 12 rectangles (6 rectangles in each facet). So ggplot expects either an overall fill color or a fill color identified for each of them.

An easier work-around than repeating the fill colors is to take advantage of aesthetic mapping by putting the fill colors for each rectangle into the rectangle data.frame.

block_rects <- data.frame(xstart_rect=c(-0.5,  0.5,  1.5,  2.5,  3.5,  4.5),
                     xend_rect=c(0.5, 1.5, 2.5, 3.5, 4.5, 5.5),
                     fill = c("#f1f1f1", "white","white","white","#f1f1f1","white"))

This allows you to map fill to a variable within the aes of geom_rect. Use scale_fill_identity so it uses the given color names.

ggplot(df, aes(x=xs,y=ys,color=cond)) + 
    geom_rect(inherit.aes = FALSE,
            data = block_rects, aes(xmin = xstart_rect, xmax = xend_rect, 
                                ymin = -Inf, ymax = Inf, fill = fill)) +
    geom_point() +
    facet_wrap(~ fs) +
    scale_fill_identity()