如何在ggplot2中调整指定facet行高并保持y轴相对刻度?
解决ggplot2分栏子图底行空白与y轴刻度对齐问题
问题描述
使用ggplot2的facet_grid()生成3列×2行共6幅子图,要求每列子图拥有独立的y轴范围。目前将每列子图保存为独立绘图后用patchwork拼接,但底行子图存在大量空白。需要修剪底行子图,仅显示y轴相关区域,关键是保持底行与上方子图的y轴相对刻度一致(例如顶行子图高度为10单位,底行子图相对高度为2单位,各图y轴范围按比例调整)。尝试拆分所有6幅子图拼接时,因facet标签保留问题导致相对图高难以计算,寻求更简便的实现方法。
解决方案
方法1:基于patchwork的拆分与比例布局
将每列的上下子图分开绘制,手动设置对应比例的y轴范围,再通过patchwork的plot_layout指定行高比例,最后拼接三列。这种方法能严格控制y轴刻度的相对一致性。
修改后的代码示例
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")) ) fillscale <- c("#0496A3", "#7D3B49", "#869D53") # A列:顶行+底行,高度比10:2 PlotA_top <- ggplot(df %>% filter(Sample == "A", Group == "Treatment")) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + scale_fill_manual(values=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_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(), axis.title.y = element_text(size = 10), axis.text.y = element_text(size = 10), plot.margin = margin(t = 0.194, r = 0.40, b = 0, l = 0.194, unit = "cm") ) + ggtitle("A") PlotA_bottom <- ggplot(df %>% filter(Sample == "A", Group == "Control")) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + scale_fill_manual(values=fillscale[[1]]) + scale_x_continuous(breaks = c(1, 1000, 1500, 2500), labels = c("label 1", "ON", "OFF", "label 2")) + ylim(0,3) + # 对应顶行0-15的1/5比例 labs(y="", x="") + theme_bw() + theme( legend.position = "none", axis.title.y = element_blank(), axis.text.y = element_text(size = 10), axis.text.x = element_text(size = 10), plot.margin = margin(t = 0, r = 0.40, b = 0.194, l = 0.194, unit = "cm") ) col_A <- PlotA_top / PlotA_bottom + plot_layout(heights = c(10, 2)) # B列:顶行+底行,高度比10:2 PlotB_top <- ggplot(df %>% filter(Sample == "B", Group == "Treatment")) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + scale_fill_manual(values=fillscale[[2]]) + scale_x_continuous(breaks = c(1, 1000, 1500, 2500), labels = c("label 1", "ON", "OFF", "label 2")) + ylim(0,20) + labs(y="", x="") + theme_bw() + theme( legend.position = "none", axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(), axis.title.y = element_blank(), axis.text.y = element_text(size = 10), plot.margin = margin(t = 0.194, r = 0.40, b = 0, l = 0, unit = "cm") ) + ggtitle("B") PlotB_bottom <- ggplot(df %>% filter(Sample == "B", Group == "Control")) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + scale_fill_manual(values=fillscale[[2]]) + scale_x_continuous(breaks = c(1, 1000, 1500, 2500), labels = c("label 1", "ON", "OFF", "label 2")) + ylim(0,4) + # 对应顶行0-20的1/5比例 labs(y="", x="") + theme_bw() + theme( legend.position = "none", axis.title.y = element_blank(), axis.text.y = element_text(size = 10), axis.text.x = element_text(size = 10), plot.margin = margin(t = 0, r = 0.40, b = 0.194, l = 0, unit = "cm") ) col_B <- PlotB_top / PlotB_bottom + plot_layout(heights = c(10, 2)) # C列:顶行+底行,高度比10:2 PlotC_top <- ggplot(df %>% filter(Sample == "C", Group == "Treatment")) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + scale_fill_manual(values=fillscale[[3]]) + scale_x_continuous(breaks = c(1, 1000, 1500, 2500), labels = c("label 1", "ON", "OFF", "label 2")) + ylim(0,30) + labs(y="", x="") + theme_bw() + theme( legend.position = "none", axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(), axis.title.y = element_blank(), axis.text.y = element_text(size = 10), plot.margin = margin(t = 0.194, r = 0.194, b = 0, l = 0, unit = "cm") ) + ggtitle("C") PlotC_bottom <- ggplot(df %>% filter(Sample == "C", Group == "Control")) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + scale_fill_manual(values=fillscale[[3]]) + scale_x_continuous(breaks = c(1, 1000, 1500, 2500), labels = c("label 1", "ON", "OFF", "label 2")) + ylim(0,6) + # 对应顶行0-30的1/5比例 labs(y="", x="") + theme_bw() + theme( legend.position = "none", axis.title.y = element_blank(), axis.text.y = element_text(size = 10), axis.text.x = element_text(size = 10), plot.margin = margin(t = 0, r = 0.194, b = 0.194, l = 0, unit = "cm") ) col_C <- PlotC_top / PlotC_bottom + plot_layout(heights = c(10, 2)) # 拼接三列 combined_plot <- col_A | col_B | col_C ggsave("~/Desktop/CombinedPlot_Fixed.pdf", combined_plot, device = "pdf", width = 7.5, height = 2.6)
方法2:使用ggh4x包的facet_grid2(更简便)
ggh4x扩展包的facet_grid2()支持设置行高比例,同时允许每列独立y轴,无需拆分子图即可实现需求。
代码示例
library(tidyverse) library(ggh4x) # 先执行 install.packages("ggh4x") 安装包 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")) ) fillscale <- c("#0496A3", "#7D3B49", "#869D53") ggplot(df) + geom_area(aes(x=Position, y=Signal, fill=Sample), col=NA, linewidth=0) + facet_grid2(Group~Sample, scales = "free_y", # 每列独立y轴 row_heights = c(10, 2)) # 设置顶行:底行高度比为10:2 scale_fill_manual(values=fillscale) + scale_x_continuous(breaks = c(1, 1000, 1500, 2500), labels = c("label 1", "ON", "OFF", "label 2")) + labs(y="Signal", x="") + theme_bw() + theme( legend.position = "none", 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() ) ggsave("~/Desktop/CombinedPlot_ggh4x.pdf", device = "pdf", width = 7.5, height = 2.6)
关键说明
- 方法1通过手动拆分和比例设置,能完全控制y轴刻度的相对一致性,适合对精度要求高的场景;
- 方法2使用ggh4x简化了代码,自动处理行高和独立轴,若无需严格固定比例,可直接让
scales="free_y"自动适配数据范围,减少手动调整。
内容的提问来源于stack exchange,提问作者JVGen
相关产品推荐
相关产品推荐

