Home > Net >  In ggplot2, how can I use multi-row x-axis labels with facet_wraps?
In ggplot2, how can I use multi-row x-axis labels with facet_wraps?

Time:10-13

I have multi-row x-axis labels such that the first row is month and the second row is year. However, I run into check_aesthetics() errors when I try to use the multi-row axis labels with facet_wrap().

Example Data:

library(data.table)
library(dplyr)
library(ggplot2)

df1 <- data.frame(matrix(ncol = 3, nrow = 12))
colnames(df1)[1:3] <- c("Date", "Group", "Value")
df1$Date <- rep(seq.Date(as.Date("2020-03-14"),as.Date("2020-08-20"),"1 month"),2)
df1$Group <- sort(rep(c("A","B"),6))
df1$Value <- rnorm(12,50,10)
df1 <- df1 %>%
  mutate(Month = month(Date),
         Year = year(Date),
         date = zoo::as.yearmon(paste(Year, Month), "%Y %m"))

df2 <- data.frame(matrix(ncol = 3, nrow = 12))
colnames(df2)[1:3] <- c("Date", "Group", "Value")
df2$Date <- rep(seq.Date(as.Date("2021-03-14"),as.Date("2021-08-20"),"1 month"),2)
df2$Group <- sort(rep(c("A","B"),6))
df2$Value <- rnorm(12,50,10)
df2 <- df2 %>%
  mutate(Month = month(Date),
         Year = year(Date),
         date = zoo::as.yearmon(paste(Year, Month), "%Y %m"))

df3 <- rbind(df1,df2)

cols <- c("A" = "#ca0020", "B" = "#0571b0")

Figure without facet_wrap() showing the multi-row x-axis

ggplot(data = df3, aes(x = factor(date), y = Value, color = Group, group = paste(Year,Group)))  
  geom_line()  
  geom_point(size = 3, aes(fill = Group), color = "black", shape = 21)  
  scale_fill_manual(values = cols)  
  scale_color_manual(values = cols)  
  scale_x_discrete(labels=substr(df3$date,1,3)) 
  labs(x = "")  
  theme_bw()  
  theme(plot.margin = unit(c(1, 1, 2, 1), "lines"),
        panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black", angle = 90, vjust = 0.5, hjust = 1),
        axis.text.y = element_text(size = 14, color = "black"),
        legend.title = element_blank(),
        legend.direction = "horizontal",
        legend.margin = margin(),
        legend.background = element_blank(),
        legend.position = c(0.1,0.93),
        panel.border = element_blank())  
  guides(fill = guide_legend(nrow = 2))  
  coord_cartesian(clip = 'off', ylim = c(0, 100))  
  annotation_custom(grid::rectGrob(gp = grid::gpar(fill = NA)))  
  annotate(geom = "text", x = c(3.5,9.5), y = -15, label = unique(df3$Year), size = 6)  
  annotate('rect',
           xmin = 6.35,
           xmax = 6.65,
           ymin = -10, ymax = 0, fill = 'white')  
  annotate('segment',
           x = c(6.35, 6.65),
           xend = c(6.35, 6.65), y = -10, yend = 0)

Now when I try to add the facet_wrap()...

ggplot(data = df3, aes(x = factor(date), y = Value, color = Group, group = paste(Year,Group)))  
  geom_line()  
  geom_point(size = 3, aes(fill = Group), color = "black", shape = 21)  
  scale_fill_manual(values = cols)  
  scale_color_manual(values = cols)  
  scale_x_discrete(labels=substr(df3$date,1,3)) 
  labs(x = "")  
  theme_bw()  
  theme(plot.margin = unit(c(1, 1, 2, 1), "lines"),
        panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black", angle = 90, vjust = 0.5, hjust = 1),
        axis.text.y = element_text(size = 14, color = "black"),
        legend.title = element_blank(),
        legend.direction = "horizontal",
        legend.margin = margin(),
        legend.background = element_blank(),
        legend.position = c(0.1,0.93),
        panel.border = element_blank())  
  guides(fill = guide_legend(nrow = 2))  
  coord_cartesian(clip = 'off', ylim = c(0, 100))  
  annotation_custom(grid::rectGrob(gp = grid::gpar(fill = NA)))  
  annotate(geom = "text", x = c(3.5,9.5), y = -15, label = unique(df3$Year), size = 6)  
  annotate('rect',
           xmin = 6.35,
           xmax = 6.65,
           ymin = -10, ymax = 0, fill = 'white')  
  annotate('segment',
           x = c(6.35, 6.65),
           xend = c(6.35, 6.65), y = -10, yend = 0)  
  facet_wrap(~Group)

...it throws the error Error in `check_aesthetics()`: ! Aesthetics must be either length 1 or the same as the data (4): label.

The error resides within annotate(geom = "text", x = c(3.5,9.5), y = -15, label = unique(df3$Year), size = 6) but I can't figure out how to fix it. I have tried changing the label = and the x = but no luck. The ideal figure would have two plots, each with multi-row x-axis labels where, similar to the example figure above, the top row is month and the second row is year. Any thoughts on how to achieve this?

CodePudding user response:

One kind of hacky way to do this is to just make two text annotations

ggplot(data = df3, aes(x = factor(date), y = Value, color = Group, group = paste(Year,Group)))  
  geom_line()  
  geom_point(size = 3, aes(fill = Group), color = "black", shape = 21)  
  scale_fill_manual(values = cols)  
  scale_color_manual(values = cols)  
  scale_x_discrete(labels=substr(df3$date,1,3)) 
  labs(x = "")  
  theme_bw()  
  theme(plot.margin = unit(c(1, 1, 2, 1), "lines"),
        panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black", angle = 90, vjust = 0.5, hjust = 1),
        axis.text.y = element_text(size = 14, color = "black"),
        legend.title = element_blank(),
        legend.direction = "horizontal",
        legend.margin = margin(),
        legend.background = element_blank(),
        legend.position = c(0.1,0.93),
        panel.border = element_blank())  
  guides(fill = guide_legend(nrow = 2))  
  coord_cartesian(clip = 'off', ylim = c(0, 100))  
  annotation_custom(grid::rectGrob(gp = grid::gpar(fill = NA)))  
  annotate(geom = "text", x = c(3.5), y = -15, label = 2020, size = 6)  
  annotate(geom = "text", x = c(9.5), y = -15, label = 2021, size = 6)  
  annotate('rect',
           xmin = 6.35,
           xmax = 6.65,
           ymin = -10, ymax = 0, fill = 'white')  
  annotate('segment',
           x = c(6.35, 6.65),
           xend = c(6.35, 6.65), y = -10, yend = 0)  
  facet_wrap(~Group)

CodePudding user response:

If you don't mind moving the year value to the strip you could use ggh4x package.

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

ggplot(data = df3, aes(x = factor(date), y = Value, color = Group, group = paste(Year,Group)))  
  geom_line()  
  geom_point(size = 3, aes(fill = Group), color = "black", shape = 21)  
  scale_fill_manual(values = cols)  
  scale_color_manual(values = cols)  
  scale_x_discrete(labels=substr(df3$date,1,3)) 
  labs(x = NULL)  
  theme_bw()  
  theme(plot.margin = unit(c(1, 1, 2, 1), "lines"),
        panel.grid = element_blank(),
        text = element_text(size = 16),
        axis.text.x = element_text(size = 14, color = "black", angle = 90, vjust = 0.5, hjust = 1),
        axis.text.y = element_text(size = 14, color = "black"),
        legend.title = element_blank(),
        legend.direction = "horizontal",
        legend.margin = margin(),
        legend.background = element_blank(),
        legend.position = c(0.1,0.90),
        panel.border = element_blank())  
  guides(fill = guide_legend(nrow = 2))  
  coord_cartesian(clip = 'off', ylim = c(0, 100))  
  facet_nested(~Group   Year, scales = "free_x")

            

Created on 2022-10-12 with reprex v2.0.2

  • Related