在空的分面中移动图例。

编程语言 2026-07-07

我在使用 facet_wrap2 为不同染色体绘制一些数据。问题是,在 chr19chr20 下方有一些空白区域,我想把图例移到那里。

因此,我在参考了这篇帖子,并且特别使用 shift_legend2() 函数,但在运行时

grid.draw(shift_legend2(p))

我总是得到如下信息:

There are no unfilled facet panels to shift legend into. Returning original plot.

这是我的代码、输出(期望值),以及数据的一个 dput()

library(grid)
library(ggh4x)
library(ggsci)
library(ggplot2)
library(cowplot)
library(ggchicklet)


p <- ggplot(excel_m) + 
  geom_chicklet(mapping=aes(x=SAMPLE, y=COUNT, fill=V_TYPE), position="dodge2", radius=grid:: unit(1, "mm")) + 
  facet_wrap2(~CHR, ncol=5, strip.position="left", scale="free_y") + theme_bw() + 
  scale_fill_npg() + guides(fill=guide_legend(title='variants', title.position='top', 
                                              title.hjust=.5, position="bottom")) +
  theme(legend.title=element_text(face='italic', size=16), legend.position='bottom', 
        panel.grid=element_blank(), axis.text.x=element_text(face='bold', 
                                                             angle=45, 
                                                             hjust=1),
        axis.title.x=element_blank(), axis.title.y=element_blank(),
        legend.text=element_text(size=12),
        axis.title=element_text(size=14), 
        axis.text=element_text(size=10)) +
  xlab(NULL) + ylab(NULL)
p

测试

excel_m <- structure(list(CHR = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 10L, 10L, 10L, 10L, 10L, 10L, 10L, 10L, 10L, 
10L, 10L, 10L, 11L, 11L, 11L, 11L, 11L, 11L, 11L, 11L, 11L, 11L, 
11L, 11L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 
12L, 13L, 13L, 13L, 13L, 13L, 13L, 13L, 13L, 13L, 13L, 13L, 13L, 
14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 15L, 
15L, 15L, 15L, 15L, 15L, 15L, 15L, 15L, 15L, 15L, 15L, 16L, 16L, 
16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 
17L, 17L, 17L, 17L, 17L, 17L, 17L, 17L, 17L, 18L, 18L, 18L, 18L, 
18L, 18L, 18L, 18L, 18L, 18L, 18L, 18L, 19L, 19L, 19L, 19L, 19L, 
19L, 19L, 19L, 19L, 19L, 19L, 19L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 20L, 20L, 20L, 20L, 20L, 20L, 20L, 20L, 20L, 
20L, 20L, 20L, 21L, 21L, 21L, 21L, 21L, 21L, 21L, 21L, 21L, 21L, 
21L, 21L, 22L, 22L, 22L, 22L, 22L, 22L, 22L, 22L, 22L, 22L, 22L, 
22L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 
4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 
5L, 5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 6L, 
6L, 6L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 7L, 8L, 8L, 
8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 8L, 9L, 9L, 9L, 9L, 9L, 9L, 
9L, 9L, 9L, 9L, 9L, 9L, 23L, 23L, 23L, 23L, 23L, 23L, 23L, 23L, 
23L, 23L, 23L, 23L), levels = c("chr1", "chr2", "chr3", "chr4", 
"chr5", "chr6", "chr7", "chr8", "chr9", "chr10", "chr11", "chr12", 
"chr13", "chr14", "chr15", "chr16", "chr17", "chr18", "chr19", 
"chr20", "chr21", "chr22", "chrX"), class = "factor"), GRAPH = c("clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt", "clone4_prt", "clone4_prt", "clone4_prt", "clone5_prt", 
"clone5_prt", "clone5_prt", "clone7_prt", "clone7_prt", "clone7_prt", 
"irradiated_prt", "irradiated_prt", "irradiated_prt", "clone4_prt", 
"clone4_prt", "clone4_prt", "clone5_prt", "clone5_prt", "clone5_prt", 
"clone7_prt", "clone7_prt", "clone7_prt", "irradiated_prt", "irradiated_prt", 
"irradiated_prt"), SAMPLE = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 
1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 
1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 
1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 
1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 
1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L), levels = c("clone4_12w", 
"clone5_12w", "clone7_12w", "irradiated_24h"), class = "factor"), 
    V_TYPE = structure(c(2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 
    2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L), levels = c("all", 
    "SNPs", "INDELs"), class = "factor"), COUNT = c(7736, 1179, 
    8915, 7737, 1178, 8915, 7724, 1178, 8902, 7737, 1179, 8916, 
    4015, 1234, 5249, 4042, 1252, 5294, 4054, 1227, 5281, 4091, 
    1269, 5360, 56568, 14994, 71562, 56505, 14984, 71489, 56567, 
    15021, 71588, 56499, 15005, 71504, 2280, 392, 2672, 2277, 
    392, 2669, 2280, 393, 2673, 2280, 391, 2671, 1194, 315, 1509, 
    1155, 312, 1467, 1238, 330, 1568, 1244, 339, 1583, 5842, 
    1311, 7153, 7214, 1527, 8741, 7267, 1586, 8853, 7117, 1491, 
    8608, 2170, 461, 2631, 2539, 485, 3024, 2689, 532, 3221, 
    2556, 485, 3041, 490, 122, 612, 506, 124, 630, 493, 124, 
    617, 495, 125, 620, 1944, 253, 2197, 1935, 253, 2188, 1934, 
    250, 2184, 1919, 252, 2171, 1437, 223, 1660, 1436, 221, 1657, 
    1437, 220, 1657, 1446, 220, 1666, 2308, 220, 2528, 2293, 
    216, 2509, 2242, 208, 2450, 2276, 217, 2493, 43954, 11844, 
    55798, 43993, 11820, 55813, 43969, 11822, 55791, 44112, 11855, 
    55967, 2305, 414, 2719, 2303, 409, 2712, 2321, 397, 2718, 
    2272, 412, 2684, 1392, 275, 1667, 1432, 274, 1706, 1709, 
    315, 2024, 1590, 284, 1874, 4535, 1079, 5614, 5119, 1203, 
    6322, 4933, 1182, 6115, 4629, 1096, 5725, 2342, 348, 2690, 
    2351, 349, 2700, 2321, 340, 2661, 2326, 350, 2676, 62163, 
    16474, 78637, 61949, 16370, 78319, 62092, 16438, 78530, 62055, 
    16364, 78419, 66217, 17925, 84142, 66188, 17884, 84072, 66283, 
    17937, 84220, 66346, 17931, 84277, 2176, 382, 2558, 2145, 
    379, 2524, 2175, 389, 2564, 2264, 386, 2650, 5047, 912, 5959, 
    5099, 918, 6017, 5107, 911, 6018, 5141, 921, 6062, 787, 138, 
    925, 792, 140, 932, 799, 142, 941, 780, 137, 917, 23656, 
    8408, 32064, 23644, 8407, 32051, 23743, 8415, 32158, 23661, 
    8397, 32058, 1215, 118, 1333, 1167, 111, 1278, 1218, 119, 
    1337, 1242, 123, 1365)), row.names = c(NA, -276L), spec = structure(list(
    cols = list(CHR = structure(list(), class = c("collector_character", 
    "collector")), GRAPH = structure(list(), class = c("collector_character", 
    "collector")), SAMPLE = structure(list(), class = c("collector_character", 
    "collector")), V_TYPE = structure(list(), class = c("collector_character", 
    "collector")), COUNT = structure(list(), class = c("collector_double", 
    "collector"))), default = structure(list(), class = c("collector_guess", 
    "collector")), delim = "\t"), class = "col_spec"), problems = <pointer: 0x835b80460>, class = c("spec_tbl_df", 
"tbl_df", "tbl", "data.frame"))

解决方案

Following and updating my comment, you can use lemon::reposition_legend() to bring the legend into one of the facets. Up front, though, we can't use facet_wrap2() since that doesn't label the empty panes in a way that lemon expects to see; fortunately I think you are not using anything that causes facet_wrap2() to look different from ggplot2::facet_wrap, so the shift is easy enough. Shift to the bottom if you want to see how I came about this.

If we start with your vanilla code, adjusted to not use facet_wrap2(), then

p <- ggplot(excel_m) + 
  geom_chicklet(mapping=aes(x=SAMPLE, y=COUNT, fill=V_TYPE), position="dodge2", radius=grid:: unit(1, "mm")) + theme_bw() + 
  facet_wrap(~CHR, ncol = 5, strip.position = "left", scales = "free_y") +
  scale_fill_npg() + guides(fill=guide_legend(title='variants', title.position='top', 
                                              title.hjust=.5, position="bottom")) +
  theme(legend.title=element_text(face='italic', size=16), legend.position='bottom', 
        panel.grid=element_blank(), axis.text.x=element_text(face='bold', 
                                                             angle=45, 
                                                             hjust=1),
        axis.title.x=element_blank(), axis.title.y=element_blank(),
        legend.text=element_text(size=12),
        axis.title=element_text(size=14), 
        axis.text=element_text(size=10)) +
  xlab(NULL) + ylab(NULL)

Now all we need to do is

lemon::reposition_legend(p, position="bottom right", panel = "panel-5-5")

同一图形来自问题,图例位于右下角的一个空窗格中,略微与 x 轴名称重叠


Why not facet_wrap2()

reposition_legend() 需要同时具备 position=(例如“右下”)和 pane=,这是面板的其中一个名称。阅读 ?reposition_legend,它友好地给出两种确定它们的方法;因为第一种方法提供了可视化参考,所以我会使用它。

lemon::gtable_show_names(p)

[ggplot2 面分面图,内部标注用于每个面板、坐标轴、引导框等;它显示右下角的两个空窗格没有名称】(/uploadfile/blog2026/65773f4b461c4d8d95a812b0907e0108.jpg)

显然,目标窗格没有名称使这件事变得困难。我没有在 facet_wrap2() 中看到一个能让这件事变得简单的选项,因此我选择使用 ggplot2::facet_wrap(),它的绘图略有不同,从 panel-19 命名法转变为 panel-{col}-{row}

同样的 ggplot2 分面图,带有组件标签,空面板有可用的名称

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章