Search code examples
rggplot2labelgeom-text

Align labels in ggplot


I am making a plot with ggplot and I am having some problems with the labels. Here is my code:

ggplot(data = prueba[prueba$Prueba=="lectura",], aes(x = año, 
                          y = Promedio, 
                          group = Tipo, 
                          color = Tipo))+
  geom_point(shape = 18,
             size = 4, position = position_dodge(0.9)) +
  geom_errorbar(aes(ymin = Promedio - Desviacion,
                    ymax = Promedio + Desviacion,
                    width = 0.4),
                position = position_dodge(0.9),
                size = 1.3,
                show.legend = F) +
  ylim(60,140) +
  scale_color_manual(values = c("#A32986",
                                "#A4589E"),
                     breaks = c("Nacional", 
                                "Sena")) +
  theme(
    panel.background = element_rect(fill = "white",
                                    color = "#C6C6C6",
                                    size = 1,
                                    linetype = "solid"),
    panel.grid.minor = element_line(size = 0.1,
                                    linetype = "dashed"),
    axis.title = element_text(color = "#575756",
                              size = 13),
    axis.text = element_text(color = "#575756",
                             size = 11),
    legend.title = element_blank(),
    legend.position = "bottom",
    legend.text = element_text(color = "#575756",
                               size = 13)
    ) + geom_text(aes(label = round(Promedio)),
                  position = position_dodge(0.9),
                  hjust = -0.5,
                  size = 5,
                  color = "#575756") +
  geom_text(aes(label = paste("(", round(Desviacion), ")")),
            color = "#575756",
            position = position_dodge(0.9),
            hjust = -0.1,
            vjust = 2,
            size = 5) + xlab(NULL)

The result is

enter image description here

As you see, the labels are not properly aligned. I would like to organize de labels, such that the upper labels are centered around the same axis as the lower labels. Does anyone know how I can do that?

My data can be reproducible by

structure(list(Tipo = c("Sena", "Sena", "Sena", "Sena", "Sena", 
"Sena", "Sena", "Sena", "Sena", "Sena", "Sena", "Sena", "Sena", 
"Sena", "Sena", "Sena", "Sena", "Sena", "Sena", "Sena", "Sena", 
"Sena", "Sena", "Sena", "Sena", "Sena", "Sena", "Sena", "Sena", 
"Sena", "Sena", "Sena", "Nacional", "Nacional", "Nacional", "Nacional", 
"Nacional", "Nacional", "Nacional", "Nacional", "Nacional", "Nacional", 
"Nacional", "Nacional", "Nacional", "Nacional", "Nacional", "Nacional", 
"Nacional", "Nacional", "Nacional", "Nacional", "Nacional", "Nacional", 
"Nacional", "Nacional", "Nacional", "Nacional", "Nacional", "Nacional", 
"Nacional", "Nacional", "Nacional", "Nacional", "Nacional", "Nacional", 
"Nacional", "Nacional", "Nacional", "Nacional"), año = structure(c(1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 
4L, 4L, 4L, 4L, 4L), .Label = c("2016", "2017", "2018", "2019"
), class = "factor"), Promedio = c(96.57, 98.03, 98.51, 100.22, 
98.17, 94.57, 98.07, 98.8, 87.38, 101.45, 99, 99.35, 96.11, 98.82, 
97.1, 95.8, 97.49, 99.86, 90.59, 97.88, 90.6, 95.13, 97.56, 98.25, 
94.03, 89.12, 91.94, 97.21, 94.77, 98.8, 99.04, 98.49, 99, 99, 
100, 100, 100, 100, 99, 100, 98, 101, 100, 96, 100, 90, 102, 
98, 100, 100, 100, 95, 132, 92, 100, 93, 102, 146, 148, 100, 
147, 96, 95, 100, 90, 98, 93, 100, 134, 143), Desviacion = c(19.96, 
20.12, 20.27, 19.78, 19.7, 18.21, 19.85, 20.17, 29.58, 22.13, 
21.98, 19.35, 20.65, 19.78, 21.17, 25.08, 22.66, 21.62, 20.9, 
20.52, 21.32, 18.83, 19.49, 21.09, 21.46, 20.54, 23.93, 21.3, 
20.09, 26.99, 19.72, 21.3, 21, 21, 21, 20, 20, 20, 21, 20, 22, 
23, 21, 22, 20, 28, 23, 25, 23, 22, 20, 22, 31, 21, 22, 22, 22, 
27, 28, 20, 28, 21, 21, 22, 21, 22, 25, 28, 30, 28), Prueba = c("mantenimiento", 
"lectura", "competencias", "promoción", "inglés", "ensamblaje", 
"razonamiento", "comunicación", "mantenimiento", "promoción", 
"comunicación", "lectura", "razonamiento", "inglés", "competencias", 
"ensamblaje", "competencias", "comunicación", "mantenimiento", 
"inglés", "razonamiento", "promoción", "ensamblaje", "lectura", 
"mantenimiento", "razonamiento", "competencias", "lectura", "promoción", 
"comunicación", "ensamblaje", "inglés", "competencias", "razonamiento", 
"inglés", "promoción", "comunicación", "mantenimiento", "lectura", 
"ensamblaje", "competencias", "comunicación", "inglés", "razonamiento", 
"lectura", "mantenimiento", "promoción", "ensamblaje", "competencias", 
"inglés", "ensamblaje", "promoción", "formar", "mantenimiento", 
"lectura", "razonamiento", "comunicación", "enseñar", "evaluar", 
"ensamblaje", "evaluar", "promoción", "mantenimiento", "inglés", 
"razonamiento", "lectura", "competencias", "comunicación", "formar", 
"enseñar")), row.names = c(56L, 57L, 58L, 59L, 60L, 61L, 62L, 
63L, 451L, 452L, 453L, 454L, 455L, 456L, 457L, 458L, 934L, 935L, 
936L, 937L, 938L, 939L, 940L, 941L, 2036L, 2037L, 2038L, 2039L, 
2040L, 2041L, 2042L, 2043L, 24800L, 24801L, 24802L, 24803L, 24804L, 
24805L, 24806L, 24807L, 68059L, 68060L, 68061L, 68062L, 68063L, 
68064L, 68065L, 68066L, 171328L, 171329L, 171330L, 171331L, 171332L, 
171333L, 171334L, 171335L, 171336L, 171337L, 171338L, 339673L, 
339674L, 339675L, 339676L, 339677L, 339678L, 339679L, 339680L, 
339681L, 339682L, 339683L), class = "data.frame")

Thanks in advance


Solution

  • I came up with the following (note that I did not use all your aesthetics, but should be enough to get the point across for one possible way to solve this).

    Putting the labels above at constant height

    ggplot(data=subset(df, Prueba=='lectura'), aes(x=año, y=Promedio, group=Tipo, color=Tipo)) +
        geom_point(shape=18, size=4, position=position_dodge(0.9)) + 
        geom_errorbar(aes(
                ymin=Promedio-Desviacion, ymax=Promedio+Desviacion, width=0.4
            ), position=position_dodge(0.9), size=1.3, show.legend=F) +
        ylim(60,140) +
        geom_text(aes(
                label = paste0(round(Promedio), "\n(", round(Desviacion), ")"), y=100),
                vjust=-4.2, position=position_dodge(0.9)
            )
    

    Giving you this:

    enter image description here

    I couldn't find a great way to align the text to the right or left of each bar. Since you are using position_dodge with your dataset, you are unable to use nudge_x, for example, to move everything "to the right" equally. It's not allowed to use both in the geom. Playing with hjust kind of works, but you can expect funny things to occur here with the two lines as you already experienced.

    Note also that I've combined your two geom_text calls into one, where you can just use paste0() or paste() to combine and "make your own label" on the fly.

    vjust use across the board allows you to put them all at the same height - which I think is what you were referring to, correct?

    Again - colors are different than yours, since I did not include your scale_color_manual and theme calls.

    Putting the Label to the Side, Nudged + Position_dodge

    If you want to put the labels to the side, you cannot do this with nudge_x, since you are unable to combine nudge_x and position arguments in geom_text. A workaround is to create a new variable to use for the label aesthetic based on df$año, then use that in your aes(... call in geom_text:

    df$año_new <- as.numeric(df$año) + 0.1 # nudge of "0.1". Had to force numeric, since + does not work on factors
    

    Then you keep everything in the original plot call, but change only the last geom_text call (note the important addition of hjust=0, which aligns everything to the left):

    geom_text(aes(
                x=año_new, y=100,
                label = paste0(round(Promedio), "\n(", round(Desviacion), ")")
            ),
            hjust=0,
            position=position_dodge(0.9)
        )
    

    enter image description here