Aligning Two Plots


In the image below, each of the 33 points/errorbars in Figure B correspond to one of the 33 bars in Figure A. Currently, the figures are in the correct order such that the first point/errorbar in Figure B corresponds to the first bar in Figure A and so on. However, the alignment of the two figures is off. Is there a way to arrange the figures such that each point/errorbar in Figure B is in alignment with its corresponding bar in Figure A? Here are the figures:

And here is a reproducible example:

lapply(c("dplyr", "ggpubr", "likert"), library, character.only=T)

data <- pisaitems[, substr(names(pisaitems), 1, 5) == "ST24Q"] 

figure_data<-likert(data, grouping=pisaitems$CNT)



figure_B<-ggplot(summary_figure_data, aes(y=mean, x=reorder(summary_figure_data$Item, desc(summary_figure_data$Item)))) +
         coord_flip() + geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), position=position_dodge2(padding=0.8)) +
         geom_point(position=position_dodge2(width=0.9)) + xlab("") + ylab("Mean/SD")

figure_final<-ggarrange(figure_A, figure_B, widths=c(1.0, 0.5), labels=c("A", "B"))

Created on 2019-05-20 by the reprex package (v0.3.0)

The code below has three major steps to allow these plots to be more easily aligned:

  1. Set up the right-hand plot so that it has the same x aesthetic and facetting as the left_hand plot.
  2. Even with corresponding x-ticks and facets, the plots will still not line up because of the legend in the left-hand plot. So, remove the legend from the left-hand plot and then lay out the legend as a separate grob (graphical object) below the left-hand plot.
  3. Add blank vertical space below the right-hand plot equal to the vertical space taken up by the legend we added in step 2.
strip.col = likert.options()$panel.strip.color

figure_data <- likert(data, grouping=pisaitems$CNT)

figure_A <- plot(figure_data)
summary_figure_data <- summary(figure_data)

figure_B <- ggplot(summary_figure_data, 
                   aes(x=mean, y=Group)) +
  geom_errorbarh(aes(xmin=mean-sd, xmax=mean+sd), height=0.3) +
  geom_point() + 
  labs(x="Mean/SD", y="") +
  facet_wrap(~Item, ncol=1) +
  theme_grey() +
  theme(strip.background=element_rect(fill=strip.col, color=strip.col),
        panel.background=element_rect(fill=NA, color="grey60"),
# Copy legend grob from figure_A
leg = get_legend(figure_A)

# Remove legend from figure_A and lay out figure_A and legend as two separate grobs
figure_A = arrangeGrob(figure_A + guides(fill=FALSE), leg, ncol=1, 

# Lay out figure_B with blank space below it
figure_B = arrangeGrob(figure_B, nullGrob(), ncol=1, heights=c(10,1))

# Lay out the two plots together
figure_final <- ggarrange(figure_A, figure_B, widths=c(1.0, 0.5), 
                          labels=c("A", "B"))


you don't need to worry about the legend with

egg::ggarrange(figure_A, figure_B, widths=c(2,1))

Amazing! This is perfect! Thank you very much.

But instead of my last block of code (the one that begins # Copy legend grob from figure_A), use @baptiste's code (which uses the ggarrange function from the egg package), which avoids the need to extract and lay out the legend.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.