0

How do I add text labels above each group's mean line (without changing the dataframe)?

library(ggplot2)
set.seed(1)
#toy data
data = data.frame(month=rep(seq(1,12),2),year = as.factor(c(rep(2019,12),rep(2020,12))),val = c(runif(12,80,100),runif(12,10,30)))

ggplot(data,aes(x=month,y=val,color=year))+
  geom_point()+
  geom_line()+
  stat_smooth(method="lm",formula = y~1,se=T)

enter image description here

Eric
  • 1,381
  • 9
  • 24

2 Answers2

2

I would create an intermediate dataset holding the means and use geom_text to display it on the plot

means <- aggregate(val ~ year, data = data, mean)

ggplot(data,aes(x=month,y=val,color=year))+
  geom_point()+
  geom_line()+
  geom_smooth(method="lm", formula = y~1, se=T) + 
  geom_text(aes(x = 7, y = val+5, label = round(val, 2), color = year), data = means) # You can change the rounding, the + value or the x position

enter image description here

It's kinda hacky since you have to set some values, by works fine for one-off plots

luizgg
  • 333
  • 3
  • 13
  • No way to do it without summarizing the dataframe? – Eric Apr 24 '20 at 19:07
  • I think that at some point you would have to create a vector or a dataframe to hold the summarized information. But if creating the object is the problem, you could pass the `aggregate` function to the `data` argument of `geom_text` – luizgg Apr 24 '20 at 21:01
1

This method uses ggplot_build to access the rightmost point in the actual geom_smooth lines to add a label above it. It's a more roundabout way of doing it, but it generalizes to other smoothing methods.

library(ggplot2)
set.seed(1)
#toy data
data = data.frame(month=rep(seq(1,12),2),
                  year = as.factor(c(rep(2019,12),rep(2020,12))),
                  val = c(runif(12,80,100),runif(12,10,30)))

p <- ggplot(data,aes(x=month,y=val,color=year))+
  geom_point()+
  geom_line()+
  stat_smooth(method="lm",formula = y~1,se=T)

library(dplyr)
p.smoothedmaxes <- 
  ggplot_build(p)$data[[3]] %>% 
  group_by( group) %>% 
  mutate( xmean = mean(x)) %>% 
  filter( x == max(x))

p +
  geom_text( data = p.smoothedmaxes, 
             mapping = aes(x = x, y = y, label = round(y,2)), 
             col = p.smoothedmaxes$colour,
             nudge_y = 7,
             inherit.aes = FALSE)

plot result

If you want the label in the middle instead, change the geom_text x mapping to x = xmean.

data[[3]] is used because stat_smooth is the third graph layer -- change as needed.

Joel Buursma
  • 118
  • 6