使用gtable替换ggplot2分面面板为自定义图表的问题求助
使用gtable替换ggplot2分面面板的问题解决
以下针对你遇到的两个问题提供具体解决方法:
问题1:增加右侧Y轴与图例的间距
方式1:调整汇总图的边距
直接在生成汇总子图时,通过theme()设置右侧边距,让子图本身留出空白,替换后自然与图例拉开距离:
sub = ggplot(mtcars) + aes(x = factor(cyl), fill = factor(am)) + geom_bar(position = "fill") + scale_fill_discrete(guide = "none") + scale_y_continuous(NULL, position = "right", labels = scales::percent) + xlab(NULL) + theme(plot.margin = margin(t = 0, r = 15, b = 0, l = 0)) # 自定义右侧边距大小
方式2:调整主图gtable的列间距
找到图例所在列,在其左侧插入空白列来增加间距:
# 先获取主图gtable gt = ggplotGrob(p) # 定位图例所在列 legend_col = gt$layout[gt$layout$name == "guide-box", "l"] # 在图例左侧添加空白列,宽度可自定义 gt = gtable_add_cols(gt, unit(10, "pt"), pos = legend_col - 1)
问题2:添加缺失的右侧轴空占位符
如果原分面图的gtable中没有axis-r-2-2这类组件,需要手动添加空占位grob来保证布局位置正确:
步骤1:确定占位符的位置
基于目标panel的布局信息,计算右侧轴应该占据的行和列范围:
panel_name = glue("panel-{row}-{col}") panel_idx = grep(panel_name, gt$layout$name) panel_layout_full = gt$layout[panel_idx, ] # 右侧轴与目标panel同高,位于panel右侧一列 axis_r_pos = list( t = panel_layout_full$t, l = panel_layout_full$r + 1, b = panel_layout_full$b, r = panel_layout_full$r + 1 )
步骤2:添加空占位符
检查gtable中是否存在目标轴条目,不存在则添加nullGrob()作为占位:
axis_r_name = glue("axis-r-{row}-{col}") if (length(grep(axis_r_name, gt$layout$name)) == 0) { gt = gtable_add_grob( gt, grobs = nullGrob(), name = axis_r_name, t = axis_r_pos$t, l = axis_r_pos$l, b = axis_r_pos$b, r = axis_r_pos$r ) } # 之后正常获取轴布局信息并替换 axis_r_idx = grep(axis_r_name, gt$layout$name) axis_r_layout = as.list(gt$layout[axis_r_idx, c("t", "l", "b", "r")])
整合后的完整代码
library(ggplot2) library(gtable) library(glue) library(purrr) # for purrr::partial get_panel = function(st, x, y) { if (missing(x) && missing(y)) { name = "panel" } else { name = glue("panel-{x}-{y}") } i = st$layout[grep(name, st$layout$name), "t"] j = st$layout[grep(name, st$layout$name), "l"] st[i, j] } get_axis = function(st, pos, x, y) { if (missing(x) && missing(y)) { name = glue("axis-{pos}") } else { name = glue("axis-{pos}-{x}-{y}") } i = st$layout[grep(name, st$layout$name), "t"] j = st$layout[grep(name, st$layout$name), "l"] if (length(c(i, j)) < 2L) { NULL } else { st[i, j] } } p = ggplot(mtcars) + aes(x = wt, y = mpg, color = factor(am)) + geom_point() + facet_wrap(~factor(cyl), ncol = 2) # 调整子图右侧边距解决间距问题 sub = ggplot(mtcars) + aes(x = factor(cyl), fill = factor(am)) + geom_bar(position = "fill") + scale_fill_discrete(guide = "none") + scale_y_continuous(NULL, position = "right", labels = scales::percent) + xlab(NULL) + theme(plot.margin = margin(t = 0, r = 15, b = 0, l = 0)) row = 2 col = 2 gt = ggplotGrob(p) st = ggplotGrob(sub) new_panel = get_panel(st) new_axis_b = get_axis(st, "b") new_axis_r = get_axis(st, "r") panel_name = glue("panel-{row}-{col}") panel_idx = grep(panel_name, gt$layout$name) panel_layout = as.list(gt$layout[panel_idx, c("t", "l", "b", "r")]) axis_b_name = glue("axis-b-{row}-{col}") axis_b_idx = grep(axis_b_name, gt$layout$name) axis_b_layout = as.list(gt$layout[axis_b_idx, c("t", "l", "b", "r")]) # 添加缺失的右侧轴占位符 axis_r_name = glue("axis-r-{row}-{col}") if (length(grep(axis_r_name, gt$layout$name)) == 0) { panel_layout_full = gt$layout[panel_idx, ] gt = gtable_add_grob( gt, grobs = nullGrob(), name = axis_r_name, t = panel_layout_full$t, l = panel_layout_full$r + 1, b = panel_layout_full$b, r = panel_layout_full$r + 1 ) } axis_r_idx = grep(axis_r_name, gt$layout$name) axis_r_layout = as.list(gt$layout[axis_r_idx, c("t", "l", "b", "r")]) # 替换面板和轴 result = gt |> partial(gtable_add_grob, !!!panel_layout)(grobs = new_panel$grob) |> partial(gtable_add_grob, !!!axis_b_layout)(grobs = new_axis_b$grob) |> partial(gtable_add_grob, !!!axis_r_layout)(grobs = new_axis_r$grob) plot(result)
内容的提问来源于stack exchange,提问作者mikeck
相关产品推荐
相关产品推荐

