Search code examples
rgeom-text

Avoid overlapping labels using geom_text


I am using geom_line to show a ratio for different countries. Is there a way to avoid the country labels overlapping on each other, or moving the ones that are overlapping horizontally a bit.

ggplot(df,aes(x=year, y = value, group = country, colour=country) ) +
  geom_line(size=1.3) +
  geom_text(data = df %>% filter(year == last(year)), aes(label = country, 
                                                                        x = year + 0.5, 
                                                                        y = value, 
                                                                        color = country), size=3) +
  guides(color = FALSE) + theme_bw()

enter image description here

Here is the data:

   df<- structure(list(year = c(2015, 2020, 2025, 2030, 2035, 2015, 2020, 
2025, 2030, 2035, 2015, 2020, 2025, 2030, 2035, 2015, 2020, 2025, 
2030, 2035, 2015, 2020, 2025, 2030, 2035, 2015, 2020, 2025, 2030, 
2035, 2015, 2020, 2025, 2030, 2035, 2015, 2020, 2025, 2030, 2035, 
2015, 2020, 2025, 2030, 2035, 2015, 2020, 2025, 2030, 2035, 2015, 
2020, 2025, 2030, 2035, 2015, 2020, 2025, 2030, 2035, 2015, 2020, 
2025, 2030, 2035, 2015, 2020, 2025, 2030, 2035, 2015, 2020, 2025, 
2030, 2035, 2015, 2020, 2025, 2030, 2035, 2015, 2020, 2025, 2030, 
2035, 2015, 2020, 2025, 2030, 2035), country = structure(c(1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 
4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L, 
7L, 7L, 8L, 8L, 8L, 8L, 8L, 9L, 9L, 9L, 9L, 9L, 10L, 10L, 10L, 
10L, 10L, 11L, 11L, 11L, 11L, 11L, 12L, 12L, 12L, 12L, 12L, 13L, 
13L, 13L, 13L, 13L, 14L, 14L, 14L, 14L, 14L, 15L, 15L, 15L, 15L, 
15L, 16L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 17L, 17L, 18L, 18L, 
18L, 18L, 18L), .Label = c("USA", "CAN", "MEX", "JPN", "ANZ", 
"EUR", "ROE", "RUS", "ASI", "CHN", "IND", "BRA", "AFR", "MES", 
"LAM", "REA", "KOR", "IDZ"), class = "factor"), value = c(19.35, 
17.77, 16.19, 15.07, 13.7, 17.91, 16.3, 14.7, 13.58, 12.58, 17.91, 
16.3, 14.7, 13.58, 12.58, 0.91, 5.9, 10.88, 17.41, 16.93, NA, 
NA, NA, NA, NA, 26.79, 24, 21.22, 19.15, 17.24, 11.11, 11.16, 
11.2, 10.93, 11.16, 18.53, 17.41, 16.29, 16.63, 19.26, 4.05, 
4.78, 5.51, 6.42, 6.95, 2.9, 3.97, 5.04, 6.32, 7.67, 2.71, 2.69, 
2.68, 3.34, 4.09, 2.53, 2.34, 2.14, 3.23, 4.25, 1.54, 1.42, 1.3, 
2.13, 2.1, 0.26, 1.83, 3.4, 3.05, 3.99, 0.8, 0.76, 0.72, 0.91, 
1.38, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 
NA)), row.names = c(NA, -90L), class = c("tbl_df", "tbl", "data.frame"
))

Solution

  • If you use the argument check_overlap = TRUE in the call to geom_text, then you would lose the overlap but also one of the overlapping labels; I guess that this is not what you want, but in case it is: here is the code and the resulting plot:

    ggplot(df,aes(x = year, y = value, group = country, colour = country) ) +
      geom_line(size = 1.3) +
      geom_text(data = df %>% filter(year == last(year)), 
                aes(label = country, x = year+0.5, y = value, color = country), 
                size = 3,
                check_overlap = TRUE) +    ###### Here is the change #######
        guides(color = FALSE) + 
      theme_bw() 
    

    enter image description here

    You can see that e.g. in the 3 top lines, the label for EUR is missing (due to check_overlap, which has been set to 'TRUE').

    As an alternative, you could use the following code. Here, geom_text() is replaced by geom_text_repel and the arguments are left unchanged. The latter function does the following according to the vignette:

    The text labels repel away from each other and away from the data points.

    The following code ...:

    ggplot(df,aes(x = year, y = value, group = country, colour = country) ) +
    geom_line(size = 1.3) +
    geom_text_repel(data = df %>% filter(year == last(year)),  #### here ####
                    aes(label = country, x = year+0.5, y = value, color = country), size = 3) +
    guides(color = FALSE) + 
    theme_bw()
    

    ... yields the following plot:

    enter image description here

    You may tweek the result by playing with hjust and vjust a little bit, as the vignette says:

    • The arguments hjust and vjust are supported, but they only control the initial positioning, so repulsive forces may disrupt alignment. Alignment with hjust will be preserved if labels only move up and down by using direction = "y". For vjust, use direction = "x".

    Please, let me know whether this is what you want.