Home > Blockchain >  R - How to align two plots with a different number of variables using ggarrange
R - How to align two plots with a different number of variables using ggarrange

Time:04-01

I'm trying to combine two plots together using the ggarrange function. I therefore have the following code:

ZINB_estimates_2 <- (list(m1 = best.mod.1, m2 = best.mod.2, m3 = best.mod.3,
                          m4 = best.mod.4)
                   %>% purrr::map_dfr(tidy, effects = "fixed", conf.int = TRUE,
                                      .id = "model")
                   %>% select(model, component, term, estimate, conf.low, conf.high)
                   ## drop conditional intercept term (not interesting)
                   %>% filter(!(term == "(Intercept)"))
                   ## create new 'term' that combines component and term
                   %>% mutate(term_orig = term,
                              term = forcats::fct_inorder(paste(term, component, sep = "_")))
                   %>% relabel_predictors(
                     c("Reports_month_prior" = "Previous number of reports",
                       "Year_numeric" = "Year",
                       "factor(Coy_Season)2" = "Season: Pup-rearing",
                       "factor(Coy_Season)3" = "Season: Dispersal",
                       "Number_4w_AC" = "Previous number of AC")))

model_names <- list(
  'zi' = "Probability of observing a week 
  with no coyote reports",
  'cond' = "Abundance of coyote reports per week"
)

model_labeller <- function(variable, value){
  return(model_names[value])
}

ZINB_estimates_2$component_2 = factor(ZINB_estimates_2$component, levels = c('zi', 'cond'))
ZINB_estimates_2$term_orig_2 = factor(ZINB_estimates_2$term_orig, levels = c('factor(Coy_Season)3',
                                                                         'factor(Coy_Season)2',
                                                                         'Year_numeric',
                                                                         'Number_4w_AC',
                                                                         'Reports_month_prior'))


p1_2 <- ggplot(ZINB_estimates_2 %>% filter(component_2 == "zi"), 
             aes(x = estimate, xmin = conf.low, xmax = conf.high, y = term_orig_2))   
  geom_pointrange(shape = 15, aes(colour = model),
                  position = position_dodge(width = 0.40))   
  facet_wrap(~component_2, labeller = model_labeller, scale = "free_x", ncol=2)  
  theme_minimal()  
  coord_capped_cart(bottom='right')  
  theme(strip.text.x = element_text(size = 12, face = "bold"),
        panel.spacing = unit(0, "lines"), legend.position = "none",
        panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        panel.border=element_blank(),
        axis.line.x = element_line(colour = "grey40", linetype = "solid"),
        axis.ticks.x = element_line(colour = "grey40", linetype = "solid"),
        plot.margin = unit(c(0, -0.1, 0, 0), "cm"))  
  xlab("Coefficient estimate")   ylab("")  
  geom_vline(xintercept = 0,
             colour = "grey80",
             linetype = 1)  
  annotate("rect", ymin = -Inf, ymax = 1.5, 
           xmin = -Inf, xmax = Inf, fill = 'grey80', alpha = 0.3)   
  annotate("rect", ymin = 2.5, ymax = 3.5, 
           xmin = -Inf, xmax = Inf, fill = 'grey80', alpha = 0.3)  
  annotate("rect", ymin = 4.5, ymax = Inf, 
           xmin = -Inf, xmax = Inf, fill = 'grey80', alpha = 0.3)   
  scale_color_lancet()  
  scale_y_discrete(labels= c("Year", 
                             "Previous number of AC", "Previous number of reports")) 


p2_2 <- ggplot(ZINB_estimates_2 %>% filter(component_2 == "cond"), 
             aes(x = estimate, xmin = conf.low, xmax = conf.high, y = term_orig_2))   
  geom_pointrange(shape = 15, aes(colour = model),
                  position = position_dodge(width = 0.40))   
  facet_wrap(~component_2, labeller = model_labeller, scale = "free_x", ncol=2)  
  coord_capped_cart(bottom='right')  
  theme_minimal()  
  theme(strip.text.x = element_text(size = 12, face = "bold"),
        panel.spacing = unit(0, "lines"), legend.position = "none",
        panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        panel.border=element_blank(),
        axis.line.x = element_line(colour = "grey40", linetype = "solid"),
        axis.ticks.x = element_line(colour = "grey40", linetype = "solid"),
        plot.margin = unit(c(0, 0, 0, 0), "cm"))  
  xlab("Coefficient estimate")   ylab("")  
  geom_vline(xintercept = 0,
             colour = "grey80",
             linetype = 1)  
  annotate("rect", ymin = -Inf, ymax = 1.5, 
           xmin = -Inf, xmax = Inf, fill = 'grey80', alpha = 0.3)   
  annotate("rect", ymin = 2.5, ymax = 3.5, 
           xmin = -Inf, xmax = Inf, fill = 'grey80', alpha = 0.3)  
  annotate("rect", ymin = 4.5, ymax = Inf, 
           xmin = -Inf, xmax = Inf, fill = 'grey80', alpha = 0.3)   
  scale_color_lancet()  
  scale_y_discrete(labels= c("Season: Dispersal", "Season: Pup-rearing", "Year", 
                             "Previous number of AC", "Previous number of reports")) 

library(egg)      
ggarrange(p1_2, p2_2  
            theme(axis.text.y = element_blank(),
                  axis.line.y = element_blank(),
                  axis.title.y= element_blank(),
                  axis.ticks.y= element_blank(),
                  panel.spacing = unit(0, "lines")), 
          nrow = 1)

Which results in the graph below.

enter image description here

However, because my number of variables differs between my two graphs, my variables don't align on the y-axis. I tried using the y axis of my second graph instead, but it still doesn't quite work. My ideal plot would look something like this (made with PowerPoint), with the y axis being the same across plots: enter image description here

I am ideally looking for a solution using ggplot2/ ggarrange, but am open to alternatives.

CodePudding user response:

One approach to achieve your desired result would be to fix the limits of the y scale in each of your plots, i.e. do limits_y <- unique(ZINB_estimates_2$term_orig_2) and use these limits in scale_y_discrete for each of your plots.

Note: As you provided no data I use some fake random example data. Also, I make use of a plotting function to make the code more minimal and reduce the duplicated code.

library(ggplot2)
library(magrittr)
library(egg)
#> Loading required package: gridExtra

limits_y <- unique(ZINB_estimates_2$term_orig_2)

p <- ZINB_estimates_2 %>%
  split(.$component_2) %>%
  lapply(plot_fun)

ggarrange(p[[2]], p[[1]]  
            theme(axis.text.y = element_blank(),
                  axis.line.y = element_blank(),
                  axis.title.y= element_blank(),
                  axis.ticks.y= element_blank(),
                  panel.spacing = unit(0, "lines")), 
          nrow = 1)

Plotting Function

plot_fun <- function(x) {
  ggplot(x, aes(x = estimate, xmin = conf.low, xmax = conf.high, y = term_orig_2))  
    geom_pointrange(
      shape = 15, aes(colour = model),
      position = position_dodge(width = 0.40)
    )  
    facet_wrap(~component_2, scale = "free_x", ncol = 2)  
    theme_minimal()  
    theme(
      strip.text.x = element_text(size = 12, face = "bold"),
      panel.spacing = unit(0, "lines"), legend.position = "none",
      panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
      panel.border = element_blank(),
      axis.line.x = element_line(colour = "grey40", linetype = "solid"),
      axis.ticks.x = element_line(colour = "grey40", linetype = "solid"),
      plot.margin = unit(c(0, -0.1, 0, 0), "cm")
    )  
    xlab("Coefficient estimate")  
    ylab("")  
    geom_vline(
      xintercept = 0,
      colour = "grey80",
      linetype = 1
    )  
    annotate("rect",
      ymin = -Inf, ymax = 1.5,
      xmin = -Inf, xmax = Inf, fill = "grey80", alpha = 0.3
    )  
    annotate("rect",
      ymin = 2.5, ymax = 3.5,
      xmin = -Inf, xmax = Inf, fill = "grey80", alpha = 0.3
    )  
    annotate("rect",
      ymin = 4.5, ymax = Inf,
      xmin = -Inf, xmax = Inf, fill = "grey80", alpha = 0.3
    )  
    scale_y_discrete(labels = c(
      A = "Year", B = "Previous number of AC", C = "Previous number of reports",
      D = "Season: Dispersal", E = "Season: Pup-rearing"
    ), limits = limits_y)
}

DATA

set.seed(123)

ZINB_estimates_2 <- data.frame(
  term_orig_2 = c(rep(LETTERS[1:3], each = 2), rep(LETTERS[1:5], each = 2)),
  model = letters[1:2],
  component_2 = c(rep("zi", 6), rep("cond", 10)),
  estimate = rnorm(16)
)
ci <- rnorm(16)
ZINB_estimates_2$conf.low <- ZINB_estimates_2$estimate - ci
ZINB_estimates_2$conf.high <- ZINB_estimates_2$estimate   ci
  • Related