Phil
Phil

Reputation: 8107

ggplot2 labels not properly being centered

I have made a chart with the following data:

library(dplyr)
library(forcats)
library(ggplot2)
library(scales)

mydf <- tibble(type = c(rep(c(rep("TypeA", 4), rep("TypeB", 4)), 2)),
               Ratings = rep(c("Top", "Middle", "Low", "NA"), 4),
               Prop = c(0.62, 0.15, 0.15, 0, 0.32, 0.16, 0.47, 0, 0.38, 0.31, 0.31, 0, 0.16, 0.16, 0.63, 0.05),
               Question = c(rep("Question1", 8), rep("Question2", 8)))
mydf$Ratings <- factor(mydf$Ratings) %>% fct_inorder() %>% fct_rev()

And here's my code to make the chart:

mydf %>% filter(Prop > 0) %>% ggplot(aes(x = Question, y = Prop, fill = Ratings)) + 
geom_bar(position = "fill", stat = "identity") + 
geom_text(aes(label = percent(round(Prop,2))), position = position_stack(vjust = 0.5)) + 
facet_grid(~ type) + scale_y_continuous(labels = percent) + 
guides(fill = guide_legend(reverse = TRUE))

It produced the following chart. I specifically used position = position_stack(vjust = 0.5) to center the labels halfway through the bars. It clearly doesn't look right for the labels for Question1. Is this a bug? Have I set my data up incorrectly?

enter image description here

Upvotes: 1

Views: 1209

Answers (1)

eipi10
eipi10

Reputation: 93811

You have position="fill" for geom_bar, but position_stack for geom_text. As a result, the top of the geom_bar stack is always 100%, but the top of the geom_text stack is whatever the sum of the values in that stack happens to be. The sums of the values for TypeA Question1 and TypeB Question1 are both less than 100%, so the height of the label stack is lower than the height of the bar stack.

For the label heights to match the bar heights, change to position_fill in geom_text. (Note, however, that since the percentages don't add up to 100% for two of the four bar stacks, using position_fill() is misleading if you don't also normalize the labels to add to 100% in each stack.)

I've also removed the last line reversing the legend so that the legend will be in the same color order as the bar sections:

mydf %>% filter(Prop > 0) %>% 
  ggplot(aes(x = Question, y = Prop, fill = Ratings)) + 
  geom_bar(position="fill", stat="identity") + 
  geom_text(aes(label = percent(round(Prop,2))), position=position_fill(vjust = 0.5)) + 
  facet_grid(~ type) + 
  scale_y_continuous(labels = percent) 

enter image description here

Upvotes: 1

Related Questions