简体   繁体   中英

Why geom_rect colours only first row of facet_wrap?

I am trying to get shaded rectangles on every even-numbered panel of my facet_wrap plot. However, when I use geom_rect , it produces the rectangles only on the second panel. I tried using annotate and geom_tile but with no success. I presume I am missing some simple detail here, probably related to the fact that my X variable is categorical and not numeric, but I am fighting this for a few hours already...

Here is my code:

even_numbers <- seq(2,nrow(df.plt),2)
ggplot(df.plt) + 
  geom_rect(data = df.plt[even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) + 
  facet_wrap(vars(Grp), ncol=1)

And the resulting plot: resulting plot with geom_rect and facet_wrap not working as expected

Edit:

I have created a dummy dataset example which replicates my issue:

set.seed(002) # just to make it reproducible
df.tmp = data.frame(nst = rnorm(100*2), Srs = sample(rep(c("S3","S4"),100)), Cnd = sample(rep(c("DN","DA","DV","DAV"),50)), Grp = sample(rep(c("close","far"),100)))

even_numbers <- seq(2,nrow(df.tmp),2)
ggplot(df.tmp) + 
  geom_rect(data = df.tmp[even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) + 
  facet_wrap(vars(Grp), ncol=1)

While your idea was right IMHO you could achieve your desired result more easily by putting the xmin and xmax values in a dataframe and by mapping on aesthetics. First note that we only need a vector of even numbers of length(unique(df.tmp$Cnd)) , ie the number of categories of Cnd . Second, as we are mixing discrete and continuous x variables I added an scale_x_discrete before geom_rect as otherwise we will get an error.

library(ggplot2)

even_numbers <- seq(2, length(unique(df.tmp$Cnd)), 2)

rects <- data.frame(
  xmin = even_numbers - 0.5,
  xmax = even_numbers + 0.5
)

ggplot(df.tmp) +
  scale_x_discrete() +
  geom_rect(
    data = rects, aes(xmin = xmin, xmax = xmax),
    ymin = -Inf, ymax = Inf, alpha = 0.3, fill = "grey"
  ) +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
  facet_wrap(vars(Grp), ncol = 1)

EDIT Just in case. The reason why your approach did not work is that the relevant part of the data used for the rects contains only the far group. The issue is that basically only rects corresponding to even numbers in the range 1 to 4 (the number of Cnd categories) are displayed. As can be seen from the following code snippet which replicates the data which under the hood is used for the rects in your approach only the far grp is present (after filtering for even numbers in the range 1 to 4):

even_numbers <- seq(2,nrow(df.tmp),2)

dplyr::bind_cols(df.tmp[even_numbers, ], data.frame(even_number = even_numbers)) |> 
  dplyr::filter(even_number <= 4)
#>          nst Srs Cnd Grp even_number
#> 1  0.1848492  S3  DV far           2
#> 2 -1.1303757  S3  DA far           4

I have been trying to understand why only the second segment gets shaded. Eventually I prepared a second example, in which I have found another possible solution. However, I am not fully satisfied as I did not understand the issue with geom_rect / facet_wrap ; I only found some workaround.

Here's the example:

# constructing the dataframe so all the combinations are present for both even and odd rows
df.tmp = data.frame(nst = rnorm(16*6), 
                    Srs = rep(c("S3", "S4"), each=8, 6), 
                    Cnd = rep(c("DN", "DA", "DV", "DAV"), each=2, 12), 
                    Grp = rep(c(rep(c("close","far"), 8), rev(rep(c("close","far"), 8))),3) )

even_numbers <- seq(2,nrow(df.tmp),2) # so the df.tmp[even_numbers, ] contains all the combinations
ggplot(df.tmp) + 
  geom_rect(data = df.tmp[even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) + 
  facet_wrap(vars(Grp), ncol=1)

As you can see here, the plot has shaded rectangles only in the second row, despite ensuring the df.tmp[even_numbers, ] includes close datapoints as well: 第一行中的非阴影段

Here I change the ggplot so it contains geom_rect separately for close and far segments:

even_numbers <- seq(2,length(unique(df.tmp$Cnd)),2) # here the df.tmp[even_numbers, ] doesn't need to have all the combinations
ggplot(df.tmp) + 
  geom_rect(data = df.tmp[df.tmp$Grp=="close", ][even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') + 
  geom_rect(data = df.tmp[df.tmp$Grp=="far", ][even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) + 
  facet_wrap(vars(Grp), ncol=1)

As you can see below, it works now: 在此处输入图像描述

As I mentioned earlier, I am still not sure why geom_rect did not work in the first place. In my solution, a separate geom_rect needs to be prepared for each segment, so it's definitely not a solution for a plot with many of them. I was trying to find a more elegant way, so one wouldn't have to bother how many segments or other groupings are declared.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM