2

I am trying to use geom_label_repel to add labels to a couple of data points on a plot. In this case, they happen to be outliers on box plots. I've got most of the code working, I can label the outlier, but for some reason I am getting multiple labels (equal to my sample size for the entire data set) mapped to that point. I'd like just one label for this outlier.

Example: enter image description here

Here is my data:

dput(sus_dev_data)
structure(list(time_point = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 
3L, 3L, 3L, 3L, 3L), .Label = c("3", "8", "12"), class = "factor"), 
    days_to_pupation = c(135L, 142L, 143L, 155L, 149L, 159L, 
    153L, 171L, 9L, 67L, 53L, 49L, 72L, 67L, 55L, 64L, 60L, 122L, 
    53L, 51L, 49L, 53L, 50L, 56L, 44L, 47L, 60L)), row.names = c(1L, 
2L, 3L, 4L, 5L, 6L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L, 
17L, 18L, 20L, 21L, 22L, 23L, 24L, 26L, 27L, 28L, 29L, 30L), class = "data.frame")

and my code...

####################################################################################################
# Time to pupation statistical analysis
####################################################################################################

## linear model
pupation_Model=lm(sus_dev_data$days_to_pupation~sus_dev_data$time_point)
pupationANOVA=aov(pupation_Model)
summary(pupationANOVA)

# Tukey test to study each pair of treatment :
pupationTUKEY <- TukeyHSD(x=pupationANOVA, which = 'sus_dev_data$time_point', 
                          conf.level=0.95)

## Function to generate significance labels on box plot
generate_label_df <- function(pupationTUKEY, variable){

  # Extract labels and factor levels from Tukey post-hoc 
  Tukey.levels <- pupationTUKEY[[variable]][,4]
  Tukey.labels <- data.frame(multcompLetters(Tukey.levels, reversed = TRUE)['Letters'])

  #I need to put the labels in the same order as in the boxplot :
  Tukey.labels$treatment=rownames(Tukey.labels)
  Tukey.labels=Tukey.labels[order(Tukey.labels$treatment) , ]
  return(Tukey.labels)
}

#generate labels using function
labels<-generate_label_df(pupationTUKEY , "sus_dev_data$time_point")

#rename columns for merging
names(labels)<-c('Letters','time_point')

# obtain letter position for y axis using means
pupationyvalue<-aggregate(.~time_point, data=sus_dev_data, max)

#merge dataframes
pupationfinal<-merge(labels,pupationyvalue) 

####################################################################################################
# Time to pupation plot
####################################################################################################

# Plot of data
(pupation_plot <- ggplot(sus_dev_data, aes(time_point, days_to_pupation)) +
  Alex_Theme +
  geom_boxplot(fill = "grey80", outlier.size = 0.75) +
  geom_text(data = pupationfinal, aes(x = time_point, y = days_to_pupation, 
                                      label = Letters),vjust=-2,hjust=.5, size = 4) +
  #ggtitle(expression(atop("Days to pupation"))) +
  labs(y = 'Days to pupation', x = 'Weeks post-hatch') +
  scale_y_continuous(limits = c(0, 200)) +
  scale_x_discrete(labels=c("3" = "13", "8" = "18",
                              "12" = "22")) +
    geom_label_repel(aes(x = 1, y = 9), 
                     label = '1')
)
Alex
  • 261
  • 2
  • 5
  • 11
  • 1
    The data for `geom_label_repel` is your whole data frame, so it is adding a label for every row. Try using a separate data frame for the label data, something like `geom_label_repel(data=data.frame(time_point=1, days_to_pupation=9), label='1')` – Kent Johnson Jan 10 '20 at 19:17

2 Answers2

6

Here's a shorter example to demonstrate what is going on. Essentially, your labels are beng recycled to be the same length as the data.

df = data.frame(x=1:5, y=1:5)

ggplot(df, aes(x,y, color=x)) +
  geom_point() +
  geom_label_repel(aes(x = 1, y = 1), label = '1')

enter image description here

You can override this by providing new data for the ggrepel

ggplot(df, aes(x,y, color=x)) +
  geom_point() +
  geom_label_repel(data = data.frame(x=1, y=1), label = '1')

enter image description here

dww
  • 30,425
  • 5
  • 68
  • 111
1

Based on your data, you have 3 outliers (one in each group), you can manually identify them by applying the classic definition of outliers by John Tukey (Upper: Q3+1.5*IQR and Lower: Q1-1.5*IQR) (but you are free to set your own rules to define an outlier). You can use the function quantile and IQR to get those points.

Here, I incorporated them in a sequence of pipe using dplyr package:

library(tidyverse)
Outliers <- sus_dev_data %>% group_by(time_point) %>% 
  mutate(Out_up = ifelse(days_to_pupation > quantile(days_to_pupation,0.75)+1.5*IQR(days_to_pupation), "Out","In"))%>%
  mutate(Out_Down = ifelse(days_to_pupation < quantile(days_to_pupation,0.25)-1.5*IQR(days_to_pupation), "Out","In")) %>%
  filter(Out_up == "Out" | Out_Down == "Out")

# A tibble: 3 x 4
# Groups:   time_point [3]
  time_point days_to_pupation Out_up Out_Down
  <fct>                 <int> <chr>  <chr>   
1 3                         9 In     Out     
2 8                       122 Out    In      
3 12                       60 Out    In   

As mentioned by @dww, you need to pass a new dataframe to geom_label_repel if you want your outliers to be single labeled. So, here we use the dataframe Outliers to feed the geom_label_repel function:

library(ggplot2)
library(ggrepel)
ggplot(sus_dev_data, aes(time_point, days_to_pupation)) +
  #Alex_Theme +
  geom_boxplot(fill = "grey80", outlier.size = 0.75) +
  geom_text(data = pupationfinal, aes(x = time_point, y = days_to_pupation, 
                                      label = Letters),vjust=-2,hjust=.5, size = 4) +
  #ggtitle(expression(atop("Days to pupation"))) +
  labs(y = 'Days to pupation', x = 'Weeks post-hatch') +
  scale_y_continuous(limits = c(0, 200)) +
  scale_x_discrete(labels=c("3" = "13", "8" = "18",
                            "12" = "22")) +
  geom_label_repel(inherit.aes = FALSE, 
                   data = Outliers,
                   aes(x = time_point, y = days_to_pupation, label = "Out"))

And you get the following graph:

enter image description here

I hope it helps you to figure it how to label all your outliers.

dc37
  • 15,840
  • 4
  • 15
  • 32
  • Thanks, this I'm sure would be useful in other cases, but the goal here wasn't to label ALL of the outliers. Instead, I wanted to highlight attributes of two specific samples (they just happened to be outliers). – Alex Jan 13 '20 at 00:30
  • Sorry for this mis-understanding, I thought you were looking for labeling outliers. So, I realized that your data contained more than one outliers and so I decided to provide this answer just in case you did not identify all of them. – dc37 Jan 13 '20 at 00:37