How to add text annotation over each boxplot of grouped data in facet_grids in ggplot2

I tried adding annotations over individual boxplots created using following data and code.

> dput(pans)
structure(list(trt = c("SP", "SP", "SP", "SP", "SP", "SP", "SP", 
"OF", "OF", "OF", "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", 
"CF", "CF", "CF", "SP", "SP", "SP", "SP", "SP", "SP", "SP", "OF", 
"OF", "OF", "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", "CF", 
"CF", "CF", "SP", "SP", "SP", "SP", "SP", "SP", "SP", "OF", "OF", 
"OF", "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", "CF", "CF", 
"CF", "SP", "SP", "SP", "SP", "SP", "SP", "SP", "OF", "OF", "OF", 
"OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", "CF", "CF", "CF"
), rep = c(1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
4, 5, 6, 7), yr = c(2018, 2018, 2018, 2018, 2018, 2018, 2018, 
2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
2018, 2018, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019
), st = c("b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", 
"b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "h", "h", "h", 
"h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", 
"h", "h", "h", "h", "h", "b", "b", "b", "b", "b", "b", "b", "b", 
"b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", 
"h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", 
"h", "h", "h", "h", "h", "h", "h", "h"), act = c(39, 41, 45, 
36, 33, 40, 41, 31, 28, 32, 36, 41, 30, 34, 30, 34, 25, 28, 27, 
32, 27, 49, 42, 41, 48, 36, 46, 45, 33, 39, 41, 36, 32, 36, 39, 
32, 33, 26, 27, 30, 31, 27, 43, 48, 42, 45, 41, 46, 44, 35, 31, 
33, 29, 36, 33, 31, 25, 35, 24, 27, 28, 30, 26, 45, 50, 52, 47, 
48, 50, 48, 31, 37, 33, 38, 35, 34, 36, 32, 28, 34, 26, 23, 30, 
30)), class = c("spec_tbl_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-84L), spec = structure(list(cols = list(trt = structure(list(), class = c("collector_character", 
"collector")), rep = structure(list(), class = c("collector_double", 
"collector")), yr = structure(list(), class = c("collector_double", 
"collector")), st = structure(list(), class = c("collector_character", 
"collector")), act = structure(list(), class = c("collector_double", 
"collector"))), default = structure(list(), class = c("collector_guess", 
"collector")), skip = 1), class = "col_spec"))

rep <- factor(pans$rep)
trt <- factor(pans$trt)
yr <- factor(pans$yr)
st <- factor(pans$st)
p <- ggplot(pans, aes(x = st, y = act))
q<-p + geom_boxplot(aes(fill = trt), position = position_dodge(0.8)) + 
  stat_summary(fun = mean, geom = "point", shape=5, size=1.5,
               aes(group = trt), position = position_dodge(0.8)) +
  scale_fill_manual(name="System", values = c("grey50","grey90", "grey100")) + 
  facet_grid(. ~ yr)+
  theme_bw()
plot<-q + scale_x_discrete(breaks=c("b","h"), labels=c("BS", "AH"))
plot2<-plot + theme(axis.title.x = element_blank())
plot2

The plotted chart is given below
pans

Then I tried adding annotation using geom_text using code

pans2 = distinct(pans, yr, st, trt) %>%
  arrange(yr, st, trt)
pans2$yloc = max(pans$act) + .05
pans2$label = c("a", "b", "a", "b", "b", "b", "a", "b", "a", "b", "b", "b")
plot2 + geom_text(data = pans2, aes(y = yloc, label = label), 
          position = position_dodge(width = .75))

However, I am getting the following error:
Don't know how to automatically pick scale for object of type function. Defaulting to continuous. Error: Aesthetics must be valid data columns. Problematic aesthetic(s): label = label. Did you mistype the name of a data column or forget to add after_stat()? Run rlang::last_error() to see where the error occurred.

Kindly help me in creating a graph with text annotations over each boxplot

thanks

Your code works for me. The original code did not place the labels in the last plot correctly because there is no aesthetic in geom_text for dodge to work with. I added color just to get it to work. Iam sure you will want to modify that.

pans <- structure(list(trt = c("SP", "SP", "SP", "SP", "SP", "SP", "SP", 
                       "OF", "OF", "OF", "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", 
                       "CF", "CF", "CF", "SP", "SP", "SP", "SP", "SP", "SP", "SP", "OF", 
                       "OF", "OF", "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", "CF", 
                       "CF", "CF", "SP", "SP", "SP", "SP", "SP", "SP", "SP", "OF", "OF", 
                       "OF", "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", "CF", "CF", 
                       "CF", "SP", "SP", "SP", "SP", "SP", "SP", "SP", "OF", "OF", "OF", 
                       "OF", "OF", "OF", "OF", "CF", "CF", "CF", "CF", "CF", "CF", "CF"
), rep = c(1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
           4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
           4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
           4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 
           4, 5, 6, 7), yr = c(2018, 2018, 2018, 2018, 2018, 2018, 2018, 
                               2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
                               2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
                               2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
                               2018, 2018, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
                               2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
                               2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
                               2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019
           ), st = c("b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", 
                     "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "h", "h", "h", 
                     "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", 
                     "h", "h", "h", "h", "h", "b", "b", "b", "b", "b", "b", "b", "b", 
                     "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b", 
                     "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", "h", 
                     "h", "h", "h", "h", "h", "h", "h", "h"), act = c(39, 41, 45, 
                                                                      36, 33, 40, 41, 31, 28, 32, 36, 41, 30, 34, 30, 34, 25, 28, 27, 
                                                                      32, 27, 49, 42, 41, 48, 36, 46, 45, 33, 39, 41, 36, 32, 36, 39, 
                                                                      32, 33, 26, 27, 30, 31, 27, 43, 48, 42, 45, 41, 46, 44, 35, 31, 
                                                                      33, 29, 36, 33, 31, 25, 35, 24, 27, 28, 30, 26, 45, 50, 52, 47, 
                                                                      48, 50, 48, 31, 37, 33, 38, 35, 34, 36, 32, 28, 34, 26, 23, 30, 
                                                                      30)), class = c("spec_tbl_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, 
                                                                                                                                                   -84L), spec = structure(list(cols = list(trt = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                              "collector")), rep = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                                                                                               "collector")), yr = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                                                                                                                                               "collector")), st = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                                                                                                                                                                               "collector")), act = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                                                                                                                                                                                                                                                "collector"))), default = structure(list(), class = c("collector_guess", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      "collector")), skip = 1), class = "col_spec"))

library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
rep <- factor(pans$rep)
trt <- factor(pans$trt)
yr <- factor(pans$yr)
st <- factor(pans$st)
p <- ggplot(pans, aes(x = st, y = act))
q<-p + geom_boxplot(aes(fill = trt), position = position_dodge(0.8)) + 
  stat_summary(fun = mean, geom = "point", shape=5, size=1.5,
               aes(group = trt), position = position_dodge(0.8)) +
  scale_fill_manual(name="System", values = c("grey50","grey90", "grey100")) + 
  facet_grid(. ~ yr)+
  theme_bw()
plot<-q + scale_x_discrete(breaks=c("b","h"), labels=c("BS", "AH"))
plot2<-plot + theme(axis.title.x = element_blank())
plot2

pans2 = distinct(pans, yr, st, trt) %>%
  arrange(yr, st, trt)
pans2$yloc = max(pans$act) + .05
pans2$label = c("a", "b", "a", "b", "b", "b", "a", "b", "a", "b", "b", "b")
plot2 + geom_text(data = pans2, aes(y = yloc, label = label,color=trt), 
                  position = position_dodge(width = .75))

Created on 2021-01-23 by the reprex package (v0.3.0)

1 Like

This is not perfect but I hope it shows you one possible approach. I hand calculated the position of the whisker of each box plot and use that to place the y position of the labels.

plot<-q + scale_x_discrete(breaks=c("b","h"), labels=c("BS", "AH"))
plot2<-plot + theme(axis.title.x = element_blank())
plot2

STATS = pans %>% group_by(yr,st, trt) %>% 
  summarize(Q75 = quantile(act, probs = 0.75),
            Q25 = quantile(act, probs = 0.25),
            MaxVal = max(act), .groups = "keep") %>% 
  mutate(WhiskUp = 1.05* (Q75 + 1.5 * (Q75 - Q25)))

pans2 = distinct(pans, yr, st, trt) %>%
  arrange(yr, st, trt) %>% 
  inner_join(STATS, by = c("yr", "st", "trt"))

#pans2$yloc = max(pans$act) + .05
pans2$label = c("a", "b", "a", "b", "b", "b", "a", "b", "a", "b", "b", "b")
plot2 + geom_text(data = pans2, aes(y = WhiskUp, label = label,color=trt), 
                  position = position_dodge(width = .75))
1 Like

Thanks a lot. It'll surely help.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.

Thanks FJCC for the solution.
Is there a way out to position the labels just near to the boxplots as in the image below
pans copy