Omry Atia
Omry Atia

Reputation: 2443

vjust inconsistent in stacked bar plot

I have a stacked bar plot, with highly unequal heights of bars. I would like to show the percentages on top of each bar.

What I have done so far is the following

df = structure(list(Type = c("Bronchoscopy", "Bronchoscopy", "Endoscopy", 
"Endoscopy"), Bacteremia = structure(c(1L, 2L, 1L, 2L), .Label = c("False", 
"True"), class = "factor"), count = c(2710L, 64L, 13065L, 103L
), perc = c(97.6928622927181, 2.3071377072819, 99.2178007290401, 
0.782199270959903)), class = c("grouped_df", "tbl_df", "tbl", 
"data.frame"), row.names = c(NA, -4L), groups = structure(list(
Type = c("Bronchoscopy", "Endoscopy"), .rows = list(1:2, 
    3:4)), row.names = c(NA, -2L), class = c("tbl_df", "tbl", 
"data.frame"), .drop = TRUE))

ggplot(df, aes(x = Type, y = perc, fill = Bacteremia)) +
geom_bar(stat = "identity") +
ylab("percent") + 
geom_text(aes(label = paste0(round(perc, 2), "%")), position = 
position_stack(vjust = -0.1), color = "black", fontface = "bold") 

I can't seem to get the vjust right. It seems like it's not behaving in the same way for the bottom versus the top bar. What I would like to achieve is to place the percentages slightly higher than the top edge of each bar.

Any ideas?

Upvotes: 0

Views: 456

Answers (2)

Raoul Duke
Raoul Duke

Reputation: 435

Here's a possible approach:

ggplot(df, aes(x = Type, y = perc, fill = Bacteremia)) +
  geom_bar(stat = "identity") +
  ylab("percent") + 
  geom_text(aes(label = paste0("", round(perc, 2), "%\n"), y = perc),
            color = "black", fontface = "bold", nudge_y = 2) 

I should elaborate that ggplot2 is going to try to place the geom_text() relative to the data. If you are trying to align horizontally the text labels, you will need to either use annotate() or supply a labelling dataset with type, percent and Bacteremia and call that in geom_text() as below.

labdf <- cbind(df, ypos = c(103, 5, 103, 5))

ggplot(df, aes(x = Type, y = perc, fill = Bacteremia)) +
  geom_bar(stat = "identity") +
  ylab("percent") + 
  geom_text(data = labdf,
            aes(label = paste0("", round(perc, 2), "%"), y = ypos, x = Type),
            color = "black", fontface = "bold") 

Upvotes: 1

cardinal40
cardinal40

Reputation: 1263

Here's one way to do it:

df <- 
  tibble(
    Type = c("Bronchoscopy", "Bronchoscopy", "Endoscopy", "Endoscopy"),
    Bacteremia = c("False", "True", "False", "True"),
    count = c(2710L, 64L, 13065L, 103L)
  ) %>% 
  group_by(Type) %>% 
  mutate(Percent = round((count / sum(count) * 100), 1))

df %>% 
  ggplot(aes(x = Type, y = Percent, fill = Bacteremia)) +
  geom_col() +
  geom_label(
    data = . %>% filter(Bacteremia == "True"), 
    aes(y = Percent + 5, label = str_c(Percent, "%")),
    show.legend = FALSE
  ) + 
  geom_label(
    data = . %>% filter(Bacteremia == "False"), 
    aes(y = 105, label = str_c(Percent, "%")),
    show.legend = FALSE
  )

The choices of 5 and 105 work on my computer, but may need to be tweaked a bit based on your specific settings and aspect ratio. The first geom_label call sets the y-axis based on the precise percentage, while the second one sets it at a constant level above the bars.

You might also want to play around with using geom_text vs. geom_label to experiment with different color and label settings. The nice thing about geom_label is that it will make it very clear which group is being labeled.

Upvotes: 1

Related Questions