Search code examples
ralignmentaxis-labelscowplotx-axis

Align another object with scatterplot+marginal boxplots


I've generated a scatterplot with marginal histograms on the x and y axes, using this extremely helpful answer from @ClausWilke (reproduced below).

library(cowplot) 
# Main plot
pmain <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species))+
  geom_point()+
  ggpubr::color_palette("jco")+
  theme(legend.position = c(0.8, 0.8))
# Marginal densities along x axis
xdens <- axis_canvas(pmain, axis = "x")+
  geom_boxplot(data = iris, aes(x = Sepal.Length, fill = Species),
               alpha = 0.7, size = 0.2)+
  ggpubr::fill_palette("jco")
# Marginal densities along y axis
# Need to set coord_flip = TRUE, if you plan to use coord_flip()
ydens <- axis_canvas(pmain, axis = "y", coord_flip = TRUE)+
  geom_boxplot(data = iris, aes(x = Sepal.Width, fill = Species),
               alpha = 0.7, size = 0.2)+
  coord_flip()+
  ggpubr::fill_palette("jco")
p1 <- insert_xaxis_grob(pmain, xdens, grid::unit(.2, "null"), position = "top")
p2<- insert_yaxis_grob(p1, ydens, grid::unit(.2, "null"), position = "right")
ggdraw(p2) 

marginal boxplot_scatterplot

This works great, but my next challenge is to use this graph as a panel in a figure, with another graph - a geom_bar plot - situated to the right. I can use plot_grid to arrange these side by side and have the x-axis aligned nicely, but I would like the height of this geom_bar chart to match only the height of the scatterplot (pmain) within the previous figure p2, instead of reaching the height of the top of the x-axis histogram in p2.

A couple of other things that would really help me out...

  • Some way of having the x-axis titles align along the bottom row (they are currently off as I've removed the x-axis labels on the bar chart)
  • Being able to separate the two graphs with a little bit of empty space

My full attempt below...

library(cowplot) 
# Main plot
pmain <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species))+
  geom_point()+
  ggpubr::color_palette("jco")+
  theme(legend.position = c(0.8, 0.8))
# Marginal densities along x axis
xdens <- axis_canvas(pmain, axis = "x")+
  geom_boxplot(data = iris, aes(x = Sepal.Length, fill = Species),
               alpha = 0.7, size = 0.2)+
  ggpubr::fill_palette("jco")
# Marginal densities along y axis
# Need to set coord_flip = TRUE, if you plan to use coord_flip()
ydens <- axis_canvas(pmain, axis = "y", coord_flip = TRUE)+
  geom_boxplot(data = iris, aes(x = Sepal.Width, fill = Species),
               alpha = 0.7, size = 0.2)+
  coord_flip()+
  ggpubr::fill_palette("jco")
p1 <- insert_xaxis_grob(pmain, xdens, grid::unit(.2, "null"), position = "top")
p2<- insert_yaxis_grob(p1, ydens, grid::unit(.2, "null"), position = "right")
ggdraw(p2) 
# generate a separate bar chart to go alongside
petal.bar <- ggplot(data=iris, aes(y=Petal.Width, x=Species, fill = Species))+
                geom_bar(stat="summary", fun="mean", position = "dodge")+
  theme(axis.text.x = element_blank(),
        legend.position = "none")+
  geom_point()+
  ggpubr::fill_palette("jco")
# place bar chart to the right 
plot_grid(p2, petal.bar, align = "h", axis = "b", nrow = 1, rel_widths = c(1, 0.6))

attempted arrangement

Any help would be greatly appreciated - thank you!


Solution

  • You could:

    1. Change element_blank to axis.text.x = element_text(colour = "white") so axis text is the same colour as the plot background so not visible, forcing the axis label to be on the vertical position as the main plot.
    2. Include a dummy plot with NULL in the call to plot_grid
      And, in response to additional question in comments...
    3. use grid::nullGrob to make the right hand side plot panel the same height as the main plot panel.
    library(ggplot2)
    library(cowplot)
    # Main plot
    pmain <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species))+
      geom_point()+
      ggpubr::color_palette("jco")+
      theme(legend.position = c(0.8, 0.8))
    # Marginal densities along x axis
    xdens <- axis_canvas(pmain, axis = "x")+
      geom_boxplot(data = iris, aes(x = Sepal.Length, fill = Species),
                   alpha = 0.7, size = 0.2)+
      ggpubr::fill_palette("jco")
    # Marginal densities along y axis
    # Need to set coord_flip = TRUE, if you plan to use coord_flip()
    ydens <- axis_canvas(pmain, axis = "y", coord_flip = TRUE)+
      geom_boxplot(data = iris, aes(x = Sepal.Width, fill = Species),
                   alpha = 0.7, size = 0.2)+
      coord_flip()+
      ggpubr::fill_palette("jco")
    p1 <- insert_xaxis_grob(pmain, xdens, grid::unit(.2, "null"), position = "top")
    p2<- insert_yaxis_grob(p1, ydens, grid::unit(.2, "null"), position = "right")
    # ggdraw(p2) 
    # generate a separate bar chart to go alongside
    petal.bar <- ggplot(data=iris, aes(y=Petal.Width, x=Species, fill = Species))+
      geom_bar(stat="summary", fun="mean", position = "dodge")+
      theme(axis.text.x = element_text(colour = "white"),
            legend.position = "none")+
      geom_point()+
      ggpubr::fill_palette("jco")
    
    p3 <- insert_xaxis_grob(petal.bar, grid::nullGrob(), grid::unit(.2, "null"), position = "top")
    # place bar chart to the right 
    plot_grid(p2, NULL, p3, align = "h", axis = "b", nrow = 1, ncol = 3, rel_widths = c(1, 0.1, 0.6))
    

    Created on 2022-01-20 by the reprex package (v2.0.1)