Calum You
Calum You

Reputation: 15072

How can I align text to bar plots with position_fill in ggplot2?

I thought I understood aligning text to plots but this one has me stumped. I want to use position_fill() to show the percentage shares of groups, but include the group counts with geom_text to indicate that different groups have different numbers of observations. Because some groups have very small shares, I decided to just have the labels be fixed at either end of the plot.

Here is my first attempt, using an example group variable long_sepal from iris that is just whether or not the Sepal.Length is bigger than 5.5.

I make a custom y_label variable that is either 0 or 1 to map to the y aesthetic of geom_text, so that the labels are always at the extremes of the plot.

library(tidyverse)
iris %>%
  mutate(long_sepal = Sepal.Length > 5.5) %>%
  count(Species, long_sepal) %>%
  mutate(y_label = if_else(long_sepal, 0, 1)) %>%
  ggplot(aes(x = Species)) +
  geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
  geom_text(
    mapping = aes(label = n, y = y_label, group = long_sepal),
    hjust = 0,
    position = position_fill()
  ) +
  coord_flip()

All well and good, but the FALSE labels are hanging off the edge of the plot. No problem, I'll just change the values of y_label:

iris %>%
  mutate(long_sepal = Sepal.Length > 5.5) %>%
  count(Species, long_sepal) %>%
  mutate(y_label = if_else(long_sepal, 0, 0.5)) %>% # This has changed
  ggplot(aes(x = Species)) +
  geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
  geom_text(
    mapping = aes(label = n, y = y_label, group = long_sepal),
    hjust = 0,
    position = position_fill()
  ) +
  coord_flip()

And nothing has changed. Interestingly, it seems to work, sort of, on changing the first value in if_else but not the second, as below. The left labels will move but don't seem to align with 0.25 as I would expect. Any ideas why? Is my mental model of how geoms work breaking here? I suspect it is something to do with position_fill but I am not sure.

iris %>%
  mutate(long_sepal = Sepal.Length > 5.5) %>%
  count(Species, long_sepal) %>%
  mutate(y_label = if_else(long_sepal, 0.25, 1)) %>% # Now the text moves, but not where I expect
  ggplot(aes(x = Species)) +
  geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
  geom_text(
    mapping = aes(label = n, y = y_label, group = long_sepal),
    hjust = 0,
    position = position_fill()
  ) +
  coord_flip()

Created on 2019-03-15 by the reprex package (v0.2.1)

Upvotes: 1

Views: 1568

Answers (1)

Calum You
Calum You

Reputation: 15072

Using the comments, especially from Axeman, I realised that I can use position_identity() to get the desired result:

library(tidyverse)
iris %>%
  mutate(long_sepal = Sepal.Length > 5.5) %>%
  count(Species, long_sepal) %>%
  ungroup() %>%
  mutate(y_label = if_else(long_sepal, 0.01, 0.99)) %>%
  ggplot(aes(x = Species)) +
  geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
  geom_text(
    mapping = aes(label = n, y = y_label, group = long_sepal),
    hjust = "inward",
    position = position_identity()
  ) +
  coord_flip()

Created on 2019-03-15 by the reprex package (v0.2.1)

Upvotes: 3

Related Questions