多列信息 ggplot y轴

4
我想在ggplot柱状图(或类似的绘图)的每个y轴标签中写入多个信息。问题是如何使所有内容都对齐。最好通过一个示例来解释我所需要的东西: enter image description here。 我的主要问题是图形左侧的格式。 到目前为止,我尝试过使用等宽字体编写标签。这基本上可行,但出于美观考虑,我想避免使用等宽字体。 我还尝试过制作几个ggplot,其中的想法是在两个初始图中除了y轴标签之外删除其他所有内容(因此这些“图”将仅是y轴标签)。然后使用grid.align将图形对齐。我在这里遇到的问题是似乎没有办法删除ggplot的图部分(或者有吗?)。这也需要一些微调,因为在“空”图之一中删除x轴标签将导致标签向下移动(因为x轴标签/标题不再占用空间)。
我还尝试过使用geom_text的方法,并使用hjust参数设置适当的距离。但是,由于某种原因,不同大小标签的间距似乎不相等(例如,“红色”和“青绿色”标签的距离在相同的hjust下不同)。由于实际数据具有许多更大的标签大小变化,这种变化使表格看起来非常杂乱......

我对标题不太担心,因为它们很容易手动添加到图中。右侧的值也不是太大的问题,因为它们具有固定的宽度,我可以使用geom_text来设置它们。所以我的主要问题是y轴(左侧)标签。

这是一个示例数据集:

dt = data.frame(shirt = c('Red','Turquoise','Red','Turquoise','Red','Turquoise','Red','Turquoise'), 
            group = c('Group alpha','Group alpha','Group beta','Group beta','Group delta','Group delta','Group gamma','Group gamma'),
            n = c(22,21,15,18,33,34,20,19),
            mean = c(1,   4,  9,  2,  4,  5 , 1, 2),
            p = c(0.1, 0.09, 0.2, 0.03, 0.05, 0.99, 0.81, 0.75))

好的,我已经添加了一些样本数据。 - Deruijter
谢谢 - 我已经删除了 dput(),只有在您没有提供创建数据框的代码时才需要它。 - Gregor Thomas
2个回答

4

我能找到的最接近的方法是使用来自ggh4x的guide_axis_nested()函数格式化左侧部分。(免责声明:我是ggh4x的作者)。使用这个轴,你不能将跨越类别(例如group)与顶部对齐,也不能为不同的层级设置标题。

library(ggplot2)
library(ggh4x)

# Create some dummy data
df <- expand.grid(
  group = paste("Group", c("alpha", "beta", "delta", "gamma")),
  shirt = c("Red", "Turquoise")
)
df$N <- sample(1:100, nrow(df))
df$mean <- rlnorm(nrow(df), meanlog = 1)
df$pvalue <- runif(nrow(df))

ggplot(df, aes(x = mean, y = interaction(N, shirt, group, sep = "&"))) +
  geom_col() +
  guides(
    y = guide_axis_nested(delim = "&"),
    y.sec = guide_axis_manual(
      breaks = interaction(df$N, df$shirt, df$group, sep = "&"),
      labels = scales::number(df$pvalue, 0.001)
    )
  ) +
  theme(
    axis.text.y.left = element_text(margin = margin(r = 5, l = 5)),
    ggh4x.axis.nesttext.y = element_text(margin = margin(r = 5, l = 5)),
    ggh4x.axis.nestline = element_blank()
  )

该示例由 reprex包 (v1.0.0) 于2021-11-16创建。


谢谢@teunbrand,这段代码比我的简单多了,所以我会记住它,将来用于没有那么严格布局要求的图形。 - Deruijter
我认为标题和对齐是很好的额外功能,一旦我有一些额外的时间,我可能会考虑将其添加到函数中。 - teunbrand

1

我认为@teunbrand提供了一个非常简洁的解决方案,代码比我的更加干净。然而,我也尝试了另一种方法,使用annotation_custom()(基于这个问题的答案)。结果非常好,很容易自定义。

dt = data.frame(shirt = c('Red','Turquoise','Red','Turquoise','Red','Turquoise','Red','Turquoise'), 
                group = c('Group alpha','Group alpha','Group beta','Group beta','Group delta','Group delta','Group gamma','Group gamma'),
                n = c(22,21,15,18,33,34,20,19),
                lvls = c(1,2,3,4,5,6,7,8),
                mean = c(1,   4,  9,  2,  4,  5 , 1, 2),
                p = c(0.1, 0.09, 0.2, 0.03, 0.05, 0.99, 0.81, 0.75))
dt$groups = paste(dt$group, dt$shirt)
dt$groups = factor(dt$groups, levels=rev(dt$groups))
p2 = ggplot(dt) +
  geom_col(aes(x=groups, y=mean)) +
  coord_flip(clip='off') + 
  theme_bw() +
  theme(axis.text.y = element_blank(),
        axis.title.y = element_blank(),
        plot.margin = unit(c(0.5,1,0,3.5), "in") # top, right, bottom, left
        )

# Compute the position on the X axis for each information column
# I wanted fixed widths for the margins, so I basically compute what the X value
# would be on a specific location of the figure.
x_size = ggplot_build(p2)$layout$panel_params[[1]]$x.range[2] - ggplot_build(p2)$layout$panel_params[[1]]$x.range[1] # length of x-axis
p_width = par()$din[1] - 4.5 # width of plot minus the margins as defined above in: plot.margin = unit(c(0.5,1,0,3.5), "in")
rel_x_size = p_width / x_size # size of one unit X in inch
col1_x = ggplot_build(p2)$layout$panel_params[[1]]$x.range[1] - (3 / rel_x_size) # the Group column, 3 inch left of the start of the plot
col2_x = ggplot_build(p2)$layout$panel_params[[1]]$x.range[1] - (1.5 / rel_x_size) # the Shirt column, 1.5 inches left of the start of the plot
col3_x = ggplot_build(p2)$layout$panel_params[[1]]$x.range[1] - (0.25 / rel_x_size) # the N column, 0.25 inches left of the start of the plot
col4_x = ggplot_build(p2)$layout$panel_params[[1]]$x.range[2] + (0.2 / rel_x_size) # the P-val column, 0.2 inches right of the end of the plot

# Set the values for each "row"
i_range = 1:nrow(dt)
i_range_rev = rev(i_range) # Because we reversed the order of the groups
for (i in i_range)  {
  if(i %% 2 == 0) {
    # Group
    p2 = p2 + annotation_custom(grob = textGrob(label = dt$group[i_range_rev[i]], hjust = 0, gp = gpar()), 
                      ymin=col1_x, ymax=col1_x, 
                      xmin=i,xmax=i)
  }
  # Shirt
  p2 = p2 + annotation_custom(grob = textGrob(label = dt$shirt[i_range_rev[i]], hjust = 0, gp = gpar()), 
                              ymin=col2_x, ymax=col2_x, 
                              xmin=i,xmax=i)
  # N
  p2 = p2 + annotation_custom(grob = textGrob(label = dt$n[i_range_rev[i]], hjust = 0, gp = gpar()), 
                              ymin=col3_x, ymax=col3_x, 
                              xmin=i,xmax=i)
  # P-val
  p2 = p2 + annotation_custom(grob = textGrob(label = dt$p[i_range_rev[i]], hjust = 0, gp = gpar()), 
                              ymin=col4_x, ymax=col4_x, 
                              xmin=i,xmax=i)
}

# Add the headers
i = i+1
p2 = p2 + annotation_custom(grob = textGrob(label = expression(bold('Group')), hjust = 0, gp = gpar()), 
                            ymin=col1_x, ymax=col1_x, 
                            xmin=i,xmax=i)
p2 = p2 + annotation_custom(grob = textGrob(label = expression(bold('Shirt')), hjust = 0, gp = gpar()), 
                            ymin=col2_x, ymax=col2_x, 
                            xmin=i,xmax=i)
p2 = p2 + annotation_custom(grob = textGrob(label = expression(bold('N')), hjust = 0, gp = gpar()), 
                            ymin=col3_x, ymax=col3_x, 
                            xmin=i,xmax=i)
p2 = p2 + annotation_custom(grob = textGrob(label = expression(bold('P-val')), hjust = 0, gp = gpar()), 
                            ymin=col4_x, ymax=col4_x, 
                            xmin=i,xmax=i)

p2

输出: enter image description here 基本上所做的是,在初始绘图中通过 plot.margin 设置图形的边距。然后进行一些计算以确定每个信息列的正确位置。随后,我们循环遍历数据集并使用annotation_custom()设置每列的值。最后,我们可以以类似的方式添加标题。
注意:如果你调整了绘图窗口的大小(例如在RStudio中),则需要重新运行代码,否则布局将会混乱。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接