ggplot2 箱线图中的标签框

Label boxes in ggplot2 boxplot

我希望在 ggplot2 生成的箱线图中的每个框上方显示一个标签。

例如:

#Example data
test = c("A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B")
patient = c(1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 3, 3)
result =  c(5, 7, 2 ,4, 6, 7, 3, 5, 5, 6, 2 ,3)
data <- tibble(test, patient, result)

#Labels I want to include
Alabs = c(1, 3, 500)
Blabs = c(8, 16, -32)

#Plot data
ggplot(data, aes(x = factor(patient), y = result, color = factor(test))) + 
  geom_boxplot(outlier.shape = 1)

给出剧情:

我想在第一个病人的红框上方打印 Alabs 的第一个元素,在第二个病人的红框上方打印 Alabs 的第二个元素, Blabs 蓝框上方为第一位患者等

我该怎么做?

采取一些作弊方法将标签放入同一个标题中:

data$labs=c(NA, 1, NA, 3, NA, 500, NA, 8, NA, 16, NA, -32) #line up the labels so each patient gets one: if you put the NAs first, labels will be at the bottom of the boxes
data$lab_x=c(NA, 0.75, NA, 1.75, NA, 2.75, NA, 1.25, NA, 2.25, NA, 3.25) #set x position for each one

然后 运行 ggplot:

 ggplot(data, aes(x = factor(patient), y = result, color = factor(test))) + 
   geom_boxplot(outlier.shape = 1)+
   geom_text(aes(label=labs, x=lab_x))

我会制作一个单独的标签数据集来添加标签。

labs = tibble(test = rep(LETTERS[1:2], each = 3),
                  patient = c(1, 2, 3, 1, 2, 3),
                  labels = c(1, 3, 500, 8, 16, -32) )

   test patient labels
  <chr>   <dbl>  <dbl>
1     A       1      1
2     A       2      3
3     A       3    500
4     B       1      8
5     B       2     16
6     B       3    -32

上面包含了x轴和分面变量的所有信息。它缺少的是有关文本在 y 轴上的位置的信息。为了将这些放在方框上方,我们可以计算每个因素组合的最大值加上 y 位置的一个小值(虽然 geom_text 有一个有用的 nudge_y 参数,但它在躲避时不起作用)。

我通过 dplyr 对每组进行汇总,然后将 y 位置值加入标签数据集。

library(dplyr)

labeldat = data %>%
     group_by(test, patient) %>%
     summarize(ypos = max(result) + .25 ) %>%
     inner_join(., labs)

现在您可以使用标签数据集添加 geom_text 图层。要以与箱线图相同的方式躲避这些,使用 position_dodge。为了防止字母出现在图例中,我使用 show.legend = FALSE.

ggplot(data, aes(x = factor(patient), y = result, color = test)) + 
     geom_boxplot(outlier.shape = 1) +
     geom_text(data = labeldat, aes(label = labels, y = ypos), 
               position = position_dodge(width = .75), 
               show.legend = FALSE )