rggplot2facet-grid

R: alter height of certain facet rows while maintaining y-axis scale


I've arranged ggplots in a facet_grid() that results in 6 plots arranged as 3 columns x 2 rows. I want each column of facets to have its own y-axis range. However, I want independent columns to have their own y-axis scale. To accomplish this, I've saved each column of facets as independent plots and then use patchwork to stitch the plots together.

You'll notice the plots in the bottom row contain a lot of white space. I would like to trim the plots in the bottom row so that only the relevant section of the y-axis is shown, but importantly, I want the relative scale of the y-axis to agree with the plot above it.

For instance, let's assume the plots in the top row occupy a height of 10 units. An appropriate relative height for the bottom row would be 2 units, and the resulting y-axis range for each plot would differ. I can manually define the new heights and ranges.

I have played with separating all 6 facets into individual plots and then stitching them together. However, I only include the facet strip text in certain plots (to recreate the image in my example), and this makes the relative plot heights difficult to calculate.

Is there an easier way to do this with ggplot2 or another package?

Thanks!

combined_plot_v1

library(tidyverse)
library(patchwork)

set.seed(42)

A1 <- sample(seq(0, 14.5, by = 0.1), 2500, replace=TRUE)
B1 <- sample(seq(0, 19.5, by = 0.1), 2500, replace=TRUE)
C1 <- sample(seq(0, 29.5, by = 0.1), 2500, replace=TRUE)

A2 <- sample(seq(0, 2.9, by = 0.01), 2500, replace=TRUE)
B2 <- sample(seq(0, 3.9, by = 0.01), 2500, replace=TRUE)
C2 <- sample(seq(0, 4.9, by = 0.01), 2500, replace=TRUE)


df <- data.frame(
  Position = rep(seq(1:2500), times = 6),
  Signal = c(A1, A2, B1, B2, C1, C2),
  Sample = factor(rep(c("A", "B", "C"), each = 5000), levels = c("A", "B", "C")),
  Group = factor(rep(c("Treatment", "Control"), each = 2500, times = 3), levels = c("Treatment", "Control"))
  )

df_A <- df %>%
  filter(Sample == "A")

df_B <- df %>%
  filter(Sample == "B")

df_C <- df %>%
  filter(Sample == "C")

fillscale <- c("#0496A3", "#7D3B49", "#869D53")

PlotA <- ggplot() +
  geom_area(data = df_A, aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) +
  facet_grid(Group~Sample) +
  scale_fill_manual(values=c(fillscale[[1]])) +
  scale_x_continuous(breaks = c(1, 1000, 1500, 2500),
                     labels = c("label 1", "ON", "OFF", "label 2")) +
  ylim(0,15) +
  labs(y="Signal", x="") +
  theme_bw() +
  theme(
    legend.position = "none", 
    axis.title.x = element_text(),
    axis.title.y = element_text(size = 10),
    axis.text.x = element_text(size = 10),
    axis.text.y = element_text(size = 10),
    strip.text.y = element_blank(),
    strip.background.y = element_blank(),
    plot.margin = margin(t = 0.194, r = 0.40, b = 0.194, l = 0.194, unit = "cm") # Default is 0.194 cm
  )

PlotB <- ggplot() +
  geom_area(data = df_B, aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) +
  facet_grid(Group~Sample) +
  scale_fill_manual(values=c(fillscale[[2]])) +
  scale_x_continuous(breaks = c(1, 1000, 1500, 2500),
                     labels = c("label 1", "ON", "OFF", "label 2")) +
  ylim(0,20) +
  labs(y="Signal", x="") +
  theme_bw() +
  theme(
    legend.position = "none", 
    axis.title.x = element_text(),
    axis.title.y = element_text(size = 10),
    axis.text.x = element_text(size = 10),
    axis.text.y = element_text(size = 10),
    strip.text.y = element_blank(),
    strip.background.y = element_blank(),
    plot.margin = margin(t = 0.194, r = 0.40, b = 0.194, l = 0.194, unit = "cm")
  )

PlotC <- ggplot() +
  geom_area(data = df_C, aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) +
  facet_grid(Group~Sample) +
  scale_fill_manual(values=c(fillscale[[3]])) +
  scale_x_continuous(breaks = c(1, 1000, 1500, 2500),
                     labels = c("label 1", "ON", "OFF", "label 2")) +
  ylim(0,30) +
  labs(y="Signal", x="") +
  theme_bw() +
  theme(
    legend.position = "none", 
    axis.title.x = element_text(),
    axis.title.y = element_text(size = 10),
    axis.text.x = element_text(size = 10),
    axis.text.y = element_text(size = 10),
    axis.ticks.length.x = unit(0, "cm"),
    plot.margin = margin(t = 0.194, r = 0.194, b = 0.194, l = 0.194, unit = "cm")
  )

combined_plot_v1 <- PlotA | PlotB | PlotC

ggsave("~/Desktop/CombinedPlotv1.pdf", combined_plot_v1, device = "pdf", width = 7.5, height = 2.6)

R 4.3.2
ggplot2 3.5.0
patchwork 1.2.0


Solution

  • You can use the ggh4x package here to easily change the individual facet heights (rows) and/or widths (cols).

    library(ggh4x)
    
    PlotA <- ggplot() + ... 
      facet_grid(Group~Sample, scales="free_y") + ... # scales="free_y"
     #ylim(0,15) + ...   # You don't want this
      force_panelsizes(rows = c(5, 1))
    

    The relative heights (5:1) can calculated from the ratio of the maximum Signals for each Group.

    summarise(df_A, max(Signal), .by=Group)
          Group max(Signal)
    1 Treatment        14.5
    2   Control         2.9
    
    14.5/2.9 # 5
    

    Do this for the other two and you should get the following:

    enter image description here