Search code examples
rggplot2plotboxplot

Automatically Adjust Vertical Position of Dodged Geom_text on Box-and-Whisker ggplot


I'm trying to automatically set the height of each of the percentage labels (which correspond the the mean)to the end of the top of each whisker (for example). Is this possible with a simple function or parameter in geom_text or elsewhere? Or, would I have to go in manually and create a geom_text for each label to set its position manually?

As you can see, the current output shows that I position_jitterdodge()'d the percentage labels, which looks chaotic and some overlap with each other, which I'd like to avoid. Alternatively, it would be nice to be able to display the percentages within each box itself.

ggplot Code:

ggplot(bcfourgroupbxplt, aes(x = Clusters, y = Proportions, 
                             fill = FourGroup)) +
  geom_point(aes(color = FourGroup, alpha = 0.5),
             position = position_jitterdodge(jitter.width = 0, 
                                             jitter.height = 0))  +
  geom_boxplot(aes(fill = FourGroup)) + 
  scale_fill_manual(name = "Group",
                    values = c("#20b0a8", "#f98110", "#FFC300", "#C70039"),
                    labels = c("Control", "B", "b.1", "b.2")) +
  scale_color_manual(values = c("Control" = "#20b0a8", "B" = "#f98110", 
  "b.1" = "#FFC300", "b.2" = "#C70039"),
                     labels = c("Control", "B", "b.1", "b.2")) +
  geom_text(aes(label = Mean, y = 0.9), 
            stat = "identity",
            position = position_jitterdodge(jitter.width = 0, 
                                            jitter.height = 0.15),
            size=2.5,
            data = bcmeanbxplt) +
  stat_summary(fun = mean, aes(alpha = 0.5), color = "black", shape = 3, 
               position = position_jitterdodge(jitter.width = 0, 
                                               jitter.height = 0)) +
  scale_x_discrete(labels = levels(finalnewbcell$Annots)) +
  xlab("Clusters") +
  ggtitle("B cell Compartment Proportions") +
  theme_clean() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
  guides(alpha = "none", color = "none")

Example structure of bcfourgroupbxplt:

A tibble: 540 × 6

Samples Clusters Proportions Group Subtype FourGroup
1 Ctrl1 Intermediate B 0.0556 Control Control Control
2 pt001 Intermediate B 0.103 ......B ....b.1 ....b.1
3 pt032 Intermediate B 0.192 ......B ....b.2 ....b.2
4 Ctrl37 Intermediate B 0.207 Control Control Control
25 pt032 Intermediate B 0.228 ......B ....b.2 ......B

--> the B group individuals repeat as I wanted to show the total B group as a box then split it by subgroup (b.1 and b.2)

Code for geom_text mean:

bcmeanbxplt <- bcfourgroupbxplt %>%
  group_by(Clusters, FourGroup) %>%
  summarize(Mean = mean(Proportions)) %>%
  ungroup() %>%
  mutate(Mean = percent(round(Mean, 3)))

Current Output

I've tried changing the position to position_dodge, or position_dodgev or position_jitterdodge, or position_nudge and playing around with those arguments to no avail (possibly because I'm using bcmeanbxplt as the data in the geom_text() layer?).

EDIT: Updated figure using solution by M.Viking
Updated figure


Solution

  • One way is to use the stat_summary() function and a helper function which returns the y location as used by the box plot geom via boxplot.stats()

    boxplot.stats(iris$Petal.Width)
    #$stats
    #[1] 0.1 0.3 1.3 1.8 2.5   # <- the 5th value is the end of the whisker
    #$n
    #[1] 150
    #$conf
    #[1] 1.10649 1.49351
    
    mean_text_fun <- function(x){return(data.frame(y = boxplot.stats(x)$stats[5]+0.1, 
                                                   label = paste0(mean(x))))}
    

    The first part of your plot code, with the iris dataset combined with stat_summary

    iris %>% 
      ggplot(aes(x = Species, y = Petal.Width, fill = Species)) +
      geom_point(aes(color = Species),alpha = 0.5,
                 position = position_jitterdodge(jitter.width = 0, jitter.height = 0))  +
      geom_boxplot() +
      stat_summary(fun.data = mean_text_fun, geom = "text")
    

    enter image description here

    Stat Summary Idea - https://stackoverflow.com/a/15720769/10276092

    Box Plot Stats Idea - https://stackoverflow.com/a/4947033/10276092