如何让ggplot2中的矩形图与直方图宽度精准对齐?
问题:如何让ggplot中的矩形图与上方柱状图宽度精准对齐
我正在制作一个ggplot可视化,需要让下方的矩形图(geom_rect)与上方的柱状图(geom_col)宽度完全对齐。当前的绘图效果、代码及数据集如下:

当前代码
sunburst <- ggplot() + scale_x_continuous(name="x") + ylim(-0.003540187, 0.004720250) + geom_rect(data=stroma_comp, mapping=aes(xmin=lower_lim, xmax=upper_lim, ymin=ymin,ymax=ymax, fill = stroma_bins), color='black', alpha=1) + geom_col(data = plot_sample, mapping = aes(x = row_id, y = cd8_density_total, fill = '#D3D3D3'), color='dark gray', alpha=1, position = position_dodge(0)) + # columns on outer level annotate("text", x = 1, y = -0.003540187, label = paste(sample_name, " ", indication, " ", pheno), size = 5, color = "black") + theme_void() + scale_fill_manual(values = c("0-10%" = "#FF7100", "10-20%" = "#eb8e00", "20-30%" = "#d4a600", "30-40%" = "#bbb908", "40-50%" = "#a1ca3c", "50-60%" = "#85d865", "60-70%" = "#66e58e", "70-80%" = "#41efb6", "80-90%" = "#0df8dd", "90-100%" = "#00ffff"), guide = "none") + scale_color_discrete(guide = "none") + theme( panel.background = element_rect(fill='transparent'), plot.background = element_rect(fill='transparent', color=NA), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), legend.background = element_rect(fill='transparent'), legend.box.background = element_rect(fill='transparent') )
数据集
# stroma_comp structure(list(stroma_bins = structure(9:10, levels = c("0-10%", "10-20%", "20-30%", "30-40%", "40-50%", "50-60%", "60-70%", "70-80%", "80-90%", "90-100%"), class = "factor"), n = c(1.09, 6.09), upper_lim = c(1.09, 7.18), lower_lim = c(0, 1.09), ymax = c(0, 0), ymin = c(-0.002360125, -0.002360125)), class = "data.frame", row.names = c(NA, -2L)) # plot_sample structure(list(Image = c("MW055 IF_Scan1.qptiff - resolution #1", "MW055 IF_Scan1.qptiff - resolution #1", "MW055 IF_Scan1.qptiff - resolution #1", "MW055 IF_Scan1.qptiff - resolution #1", "MW055 IF_Scan1.qptiff - resolution #1", "MW055 IF_Scan1.qptiff - resolution #1", "MW055 IF_Scan1.qptiff - resolution #1" ), Name = c("Tile 6", "Tile 3", "Tile 5", "Tile 2", "Tile 7", "Tile 4", "Tile 1"), tumor_stroma_epi_nsclc_v2..Epithelium.area.µm.2 = c(98.9314, 8.9938, 78.9453, 165.885, 43.9696, 82.9425, 55.9612), tumor_stroma_epi_nsclc_v2..Necrosis.area.µm.2 = c(18.9868, 0, 1.9986, 116.919, 0, 0, 0), tumor_stroma_epi_nsclc_v2..Stroma.area.µm.2 = c(50773.8184, 3895.3008, 29928.2617, 71677.3322, 58207.6645, 15441.3, 21588.0416 ), tumor_stroma_epi_nsclc_v2..Tumor.area.µm.2 = c(7216, 56.9605, 340.7639, 3572.5244, 1564.9156, 1187.1774, 1162.1947), Area.µm.2 = c(94309.7, 11209.7, 46866.9, 102262.6, 88348.2, 17987.8, 57560.1), ID = c("MW055 IF_Scan1.qptiff - resolution #1-Tile 6", "MW055 IF_Scan1.qptiff - resolution #1-Tile 3", "MW055 IF_Scan1.qptiff - resolution #1-Tile 5", "MW055 IF_Scan1.qptiff - resolution #1-Tile 2", "MW055 IF_Scan1.qptiff - resolution #1-Tile 7", "MW055 IF_Scan1.qptiff - resolution #1-Tile 4", "MW055 IF_Scan1.qptiff - resolution #1-Tile 1" ), num_tumor_detections = c(52, 0, 0, 29, 11, 6, 12), tumor_cd45 = c(13, 0, 0, 16, 8, 6, 8), tumor_cd8 = c(4, 0, 0, 0, 0, 0, 1), tumor_epi = c(35, 0, 0, 12, 3, 0, 3), tumor_stromal = c(0, 0, 0, 1, 0, 0, 0), num_stroma_detections = c(290, 16, 172, 350, 298, 105, 117), stroma_cd45 = c(163, 14, 117, 168, 184, 72, 80), stroma_cd8 = c(11, 0, 5, 16, 13, 4, 5), stroma_epi = c(10, 1, 3, 19, 0, 3, 2), stroma_stromal = c(106, 1, 47, 147, 101, 26, 30), num_epi_detections = c(0, 0, 0, 0, 0, 0, 0), epi_cd45 = c(0, 0, 0, 0, 0, 0, 0), epi_cd8 = c(0, 0, 0, 0, 0, 0, 0), epi_epi = c(0, 0, 0, 0, 0, 0, 0), epi_stromal = c(0, 0, 0, 0, 0, 0, 0), num_necro_detections = c(0, 0, 0, 0, 0, 0, 0), necro_cd45 = c(0, 0, 0, 0, 0, 0, 0), necro_cd8 = c(0, 0, 0, 0, 0, 0, 0), necro_epi = c(0, 0, 0, 0, 0, 0, 0), necro_stromal = c(0, 0, 0, 0, 0, 0, 0), total_cells = c(342, 16, 172, 379, 309, 111, 129), total_cd8 = c(15, 0, 5, 16, 13, 4, 6), total_epithelial = c(45, 1, 3, 31, 3, 3, 5), tissue_area = c(58107.7366, 3961.2551, 30349.9695, 75532.6606, 59816.5497, 16711.4199, 22806.1975), cd8_percent = c(0.043859649122807, 0, 0.0290697674418605, 0.0422163588390501, 0.0420711974110032, 0.036036036036036, 0.0465116279069767), cd8_density_stroma = c(0.000216647089910417, 0, 0.000167066168096225, 0.000223222593655627, 0.000223338285630752, 0.000259045546683246, 0.000231609707478051), cd8_density_tumor = c(0.000554323725055432, 0, 0, 0, 0, 0, 0.000860441025931369), cd8_density_total = c(0.000258141185282374, 0, 0.000164744811358048, 0.000211828894585503, 0.000217331157768199, 0.000239357279269848, 0.000263086382550182), stroma_percentage = c(0.873787577539202, 0.983350150814574, 0.986105165608157, 0.948958127922744, 0.973103008982145, 0.923996889097377, 0.94658662848114), cd8_density_ratio = c(2.55864837734328, NaN, 0, 0, 0, 0, 3.71504733243062), stroma_bins = structure(c(9L, 10L, 10L, 10L, 10L, 10L, 10L), levels = c("0-10%", "10-20%", "20-30%", "30-40%", "40-50%", "50-60%", "60-70%", "70-80%", "80-90%", "90-100%"), class = "factor"), cd8_percent_bins = structure(c(3L, 1L, 2L, 3L, 3L, 2L, 3L), levels = c("0-2% CD8+ Cells", "2-4% CD8+ Cells", "4-6% CD8+ Cells", "6-8% CD8+ Cells", "8-10% CD8+ Cells", "10-15% CD8+ Cells", "15-20% CD8+ Cells", ">20% CD8+ Cells"), class = "factor"), row_id = 1:7), row.names = c(1584L, 1581L, 1583L, 1580L, 1585L, 1582L, 1579L), class = "data.frame")
解决方案
要让geom_rect和geom_col宽度精准对齐,核心是统一两者的x轴范围,让矩形的总宽度与柱状图的总宽度完全匹配,具体步骤如下:
1. 计算柱状图的实际x轴边界
默认geom_col的柱子宽度为0.9,每个柱子中心对应row_id(1到7),因此:
- 柱状图左边界:
min(plot_sample$row_id) - 0.45 = 0.55 - 柱状图右边界:
max(plot_sample$row_id) + 0.45 = 7.45 - 柱状图总宽度:
7.45 - 0.55 = 6.9
2. 调整stroma_comp的x范围匹配柱状图
根据stroma_comp中n的比例,拆分柱状图的总宽度,计算每个矩形的新x边界:
library(dplyr) col_left <- min(plot_sample$row_id) - 0.45 col_right <- max(plot_sample$row_id) + 0.45 total_width <- col_right - col_left # 计算每个区间的宽度比例,生成新的xmin/xmax stroma_comp <- stroma_comp %>% mutate(proportion = n / sum(n), new_xmin = col_left + cumsum(c(0, head(proportion, -1)))*total_width, new_xmax = col_left + cumsum(proportion)*total_width)
3. 修改绘图代码,使用新的x边界
将geom_rect中的lower_lim/upper_lim替换为计算出的new_xmin/new_xmax,同时统一x轴范围:
sunburst <- ggplot() + scale_x_continuous(name="x", limits = c(col_left, col_right), expand = c(0,0)) + ylim(-0.003540187, 0.004720250) + geom_rect(data=stroma_comp, mapping=aes(xmin=new_xmin, xmax=new_xmax, ymin=ymin,ymax=ymax, fill = stroma_bins), color='black', alpha=1) + geom_col(data = plot_sample, mapping = aes(x = row_id, y = cd8_density_total, fill = '#D3D3D3'), color='dark gray', alpha=1, position = position_dodge(0), width = 0.9) + # 显式指定宽度,避免默认变化 annotate("text", x = col_left + 0.5, y = -0.003540187, label = paste(sample_name, "\n", indication, "\n", pheno), size = 5, color = "black") + theme_void() + scale_fill_manual(values = c("0-10%" = "#FF7100", "10-20%" = "#eb8e00", "20
相关产品推荐
相关产品推荐

