GGPLOT Align Labels in Stacked Barplot with Subset of Data

Viewed 391
library(tidyverse)
library(ggplot2)
library(scales)

df <- data.frame("class" = rep("class_1", 13), 
                 "grade" = c("A+", "A", "A-",
                             "B+", "B", "B-",
                             "C+", "C", "C-",
                             "D+", "D", "D-", "F"),
                 "n" = c(24, 29, 28, 9, 12, 5, 1, 2, 5, 1, 1, 2, 4)) %>% 
  mutate(Percent = n/sum(n))


df %>% 
  ggplot(aes(x=class, y=Percent, fill=grade)) +
  geom_bar(position = "stack", stat = "identity", colour = "grey43") +
  scale_y_continuous(labels = scales::percent) +
  xlab("") +
  ylab("Percent")+
  geom_text(data = subset(df, Percent > 0.02),
            aes(y = Percent, label = paste0(round(Percent*100, 1),"%")),
            stat = "identity",
            size = 4,
            position = position_stack(vjust = 0.5),
            fontface = 2,
            family = "Courier New")

This is what the barplot looks like if I do NOT subset the data: enter image description here

This is what the barplot looks like after I subset the data: enter image description here

As you can see in the subset code:

data = subset(df, Percent > 0.02)

I am trying to eliminate the labels for bar segments less than 2% so as to not jam all the labels in tiny bar segments. But when I do that, the labels in the other bar segments move.

I assumed that position = position_stack(vjust = 0.5) dropped the label in the middle of the segment, but apparently that's not the case. The label positions seem to be dependent upon each other.

How do I get my labels to position themselves independent of labels that may have been eliminated?

I tried dplyr::filter(), but the labels behave the same way as they do when using subset().

Please note that I am trying to solve this specific problem with the labels. I am not interested in combining levels, e.g. with forcats::fct_lump()

I also do not want to eliminate any of the factor levels of "grade". I want to show, for example, that there were some students who made a 'D' in the class, but I want to remove the Percent label for aesthetic reasons.

1 Answers

As an alternative for subsetting the data, you can wrap your text label in an ifelse statement that substitutes small values with empty strings.

Example below:

library(tidyverse)
library(ggplot2)
library(scales)

df <- data.frame("class" = rep("class_1", 13), 
                 "grade" = c("A+", "A", "A-",
                             "B+", "B", "B-",
                             "C+", "C", "C-",
                             "D+", "D", "D-", "F"),
                 "n" = c(24, 29, 28, 9, 12, 5, 1, 2, 5, 1, 1, 2, 4)) %>% 
  mutate(Percent = n/sum(n))


df %>% 
  ggplot(aes(x=class, y=Percent, fill=grade)) +
  geom_bar(position = "stack", stat = "identity", colour = "grey43") +
  scale_y_continuous(labels = scales::percent) +
  xlab("") +
  ylab("Percent")+
  geom_text(
    data = df,
    aes(y = Percent, 
        label = ifelse(Percent > 0.02, paste0(round(Percent*100, 1),"%"), "")),
    stat = "identity",
    size = 4,
    position = position_stack(vjust = 0.5),
    fontface = 2,
    family = "Courier New"
  )
#> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
#> font family not found in Windows font database

Created on 2021-03-17 by the reprex package (v0.3.0)

Related