Amstell Amstell - 1 month ago 11
R Question

Prevent overlapping labels with geom_segment and facet

I'm using

geom_segment()
and
facet_wrap()
to display some estimates across different types and models. A previous post helped me organize things, but I'm struggling to figure out how to overset the labels so they don't overlap. Things get messy once I add more models for comparison with more data points. I've tried changing the aspect ratio without resolution.

How can I overset, or spread the labels out so they are readable while retaining the scale of y-axis for comparison across models and types?

Sample data

dat <- structure(list(temp = c(1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3,
4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4,
5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5,
1, 2, 3, 4, 5), rev = c(-5, -11, -20, -29, -40, -9, -20, -32,
-45, -57, -12, -24, -37, -50, -62, -7, -20, -36, -52, -67, -5,
-13, -23, -35, -47, -12, -24, -36, -48, -58, 0, 0, -3, -7, -12,
0, 0, 0, 0, -1, -4, -9, -15, -21, -28, 2, 1, -1, -6, -13, -4,
-7, -8, -8, -6, 8, 16, 23, 29, 34), type = c("Type 1", "Type 1",
"Type 1", "Type 1", "Type 1", "Type 1", "Type 1", "Type 1", "Type 1",
"Type 1", "Type 1", "Type 1", "Type 1", "Type 1", "Type 1", "Type 2",
"Type 2", "Type 2", "Type 2", "Type 2", "Type 2", "Type 2", "Type 2",
"Type 2", "Type 2", "Type 2", "Type 2", "Type 2", "Type 2", "Type 2",
"Type 3", "Type 3", "Type 3", "Type 3", "Type 3", "Type 3", "Type 3",
"Type 3", "Type 3", "Type 3", "Type 3", "Type 3", "Type 3", "Type 3",
"Type 3", "Type 4", "Type 4", "Type 4", "Type 4", "Type 4", "Type 4",
"Type 4", "Type 4", "Type 4", "Type 4", "Type 4", "Type 4", "Type 4",
"Type 4", "Type 4"), model = c("A", "A", "A", "A", "A", "B",
"B", "B", "B", "B", "C", "C", "C", "C", "C", "A", "A", "A", "A",
"A", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "A", "A",
"A", "A", "A", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C",
"A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "C", "C", "C",
"C", "C")), .Names = c("temp", "rev", "type", "model"), row.names = c(NA,
-60L), class = "data.frame")


Plot

df.labeled <- dat %>%
ungroup() %>% group_by(type, rev) %>%
mutate(label = c(rev[1], rep(NA, length(rev) - 1)))

ggplot(df.labeled, aes(temp, rev, color = model)) +
geom_segment(aes(xend = 0, yend = rev), linetype = "dashed", color = "grey") +
geom_text(aes(label = label, x = -0.1), colour = "black", hjust = 1) +
geom_vline(xintercept = 0) +
geom_point() + geom_line() + facet_wrap(~type) +
scale_y_continuous(breaks = NULL) +
scale_x_continuous(limits = c(-0.5, NA)) +
theme_bw() + theme(panel.grid = element_blank())


enter image description here

Answer Source

One option would be to stagger the values. However, I think labeling the points directly is cleaner and less confusing. I show both methods below.

Staggered value labels

# Set up staggered x-values for the value labels
df.labeled = df.labeled %>% 
  group_by(type, is.na(label)) %>% 
  arrange(rev) %>%
  mutate(xval = rep(c(-0.1,-0.35), ceiling(n()/2))[1:n()])

ggplot(df.labeled, aes(temp, rev, color = model)) + 
  geom_segment(aes(xend=xval - 0.08, yend=rev), linetype="11", color="grey70", size=0.3) +
  geom_text(aes(x=xval, label=label), colour="black", hjust=1, size=2.5) +
  geom_vline(xintercept = 0, colour="grey90", size=0.3) +
  geom_point() + geom_line() + facet_wrap(~type) + 
  scale_y_continuous(breaks = NULL) + 
  scale_x_continuous(limits = c(-0.5, NA)) +
  theme_bw() + theme(panel.grid = element_blank())

enter image description here

Use y-values as the point labels

ggplot(df.labeled, aes(temp, rev, color = model)) + 
  geom_text(aes(label = rev), size=2.5, show.legend=FALSE) +
  geom_line(alpha=0.3, size=0.7) + 
  facet_wrap(~type) + 
  scale_x_continuous(limits = c(0, NA)) +
  theme_bw() + 
  theme(panel.grid = element_blank()) +
  guides(colour=guide_legend(override.aes=list(alpha=1, size=1)))

enter image description here