rggplot2labelaxis-labels

In R, ggplot for a population pyramid: how to align labels near to the axis with geom_bar geom_label after flipping the coordinates


I am making a sort of population pyramid using ggplot (plotrix doesn't allow me to do fancy labels etc), then I start with a geom_bar with labels and later I flip the coordinates. Sadly, labels almost cannot being seeing. I would like to move those labels near to the "y- axis" in the middle, that now is showing the age groups. Data is here:

d <- data.frame(age.grp2 = c("1-10", "11-20", "21-30", "31-40", "41-50", "1-10", "11-20", "21-30", "31-40", "41-50"), 
                sex = c("Female","Female","Female","Female","Female","Male","Male","Male","Male","Male" ),
                n.enroll = c(288,500,400,300,200,300,460,300,200,300),
                proportion = c(17.1,29.6,23.7,17.8,11.8,51,47.9,42.9,40,60),
                proportion2 = c(-17.1,-29.6,-23.7,-17.8,-11.8,51,47.9,42.9,40,60))

My code is this one:

ggplot(d, aes(x = age.grp2, y = proportion2, fill = sex)) +
    geom_bar(position = position_dodge(width=1), stat='identity') + 
    geom_label(aes(label = paste(n.enroll," (",proportion,"%)", sep=""), group = factor(sex)), 
               fill="white", colour = "black", 
               position= position_dodge(width=1), 
               size = 3) +
    scale_fill_manual(values=c("#BFD5E3", "grey")) + 
    facet_share(~sex, dir = "h", scales = "free", reverse_num = TRUE) +
    coord_flip() +
    theme(panel.grid.major = element_blank(), 
          panel.grid.minor = element_blank(),
          #panel.border = element_blank(),
          panel.background = element_blank(),
          legend.position = "none",
          #axis.line.x  = element_line(color = "black"),
          axis.ticks.y = element_blank(),
          axis.text.x  = element_text(colour = "black", size = 8, face = "bold", angle=0, hjust=0.5),
          axis.text.y  = element_text(colour = "black", size = 8, face = "bold"),
          axis.title.x = element_text(size = 14,  face="bold", margin = margin(t = 30, r = 20, b = 10, l = 20)),
          plot.margin  = unit(c(1,1,1,1),"cm")) + 
    labs(y = "Enrollment percentage within sex",x="")

I am attaching also the plot, where we can see in females the label in the age group 11-20 is cut. I would like to have all labels near to the age group labels, within each bar: female labels moved to the right and male labels move it to the left. Also, I would like to have each x-axis extended to 100% or at least in same range, in females goes up to 30% and in males goes up to 60%. Thanks for all the comments
enter image description here


Solution

  • Here's a minimal solution using the base ggplot package, without most of your formatting. The key part is to add a conditional y = ... into the geom_label(aes()) section:

    d %>% 
      mutate(
        label = str_c(n.enroll, " (", proportion, "%)"),
        label_loc = if_else(sex == "Female", -9.5, 3),
        proportion_for_chart = if_else(sex == "Female", -proportion, proportion)
      ) %>% 
      ggplot(aes(x = age.grp2, y = proportion_for_chart, fill = sex)) +
      geom_col(show.legend = FALSE) +
      geom_label(aes(y = label_loc, label = label), size = 3, fill = "white", hjust = 0) +
      coord_flip() +
      facet_wrap(~ sex, scales = "free") +
      theme(
        axis.title = element_blank()
      )
    

    Whenever possible, I try to reshape data and use geom_col rather than try to get lucky with geom_bar. You should be able to play around with different hard-coded values of y in the geom_label call to fix the proper location for your labels based on your formatting and image size/scale.

    enter image description here