rggplot2geom-text

Geom_text above grouped geom_bar when theres negative values present


Consider this data:

# A tibble: 15 x 3
   group variable value
   <int> <fct>    <int>
 1     1 1            0
 2     1 2            0
 3     1 3           -2
 4     2 1            1
 5     2 2           -2
 6     2 3            1
 7     3 1           -1
 8     3 2           -2
 9     3 3           -2
10     4 1            0
11     4 2           -1
12     4 3            2
13     5 1            0
14     5 2            1
15     5 3           -2

library(tidyverse)

df %>% 
  ggplot() + 
  aes(x = group ,weight = value) + 
  geom_bar(aes(y = after_stat(proportions(count)), fill = variable), 
           stat = "count") + 
  coord_flip() +
  geom_hline(yintercept = 0) + 
  geom_text(aes(y = after_stat(proportions(count)),
                label = str_c(percent(after_stat(proportions(count))), 
                              " | ", after_stat(count))), 
            stat = "count")

The code yields the plot with the text at y = group proportion of the whole dataset. What I would like is the text to be above the bars, i.e above the positive values. How can I achieve this with geom_bar()?

enter image description here

Dummy data:

structure(list(group = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 
4L, 4L, 4L, 5L, 5L, 5L), variable = structure(c(1L, 2L, 3L, 1L, 
2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L), levels = c("1", 
"2", "3"), class = "factor"), value = c(0L, 0L, -2L, 1L, -2L, 
1L, -1L, -2L, -2L, 0L, -1L, 2L, 0L, 1L, -2L)), row.names = c(NA, 
-15L), class = c("tbl_df", "tbl", "data.frame"))

Solution

  • The only option I have found to achieve your desired result would be to aggregate the data for the labels outside of the ggplot() pipeline.

    library(tidyverse)
    library(scales)
    
    df_label <- df |>
      summarise(
        count = sum(value),
        .by = c(group, variable)
      ) |> 
      mutate(
        prop = proportions(count),
        prop_pos = ifelse(count < 0, prop, 0)
      ) |> 
      summarise(
        label = paste0(percent(sum(prop)), " | ", sum(count)),
        y = sum(prop_pos),
        .by = group
      )
    
    df %>%
      ggplot() +
      aes(x = group, weight = value) +
      geom_bar(
        aes(y = after_stat(proportions(count)), fill = variable),
        stat = "count"
      ) +
      coord_flip() +
      geom_hline(yintercept = 0) +
      geom_text(
        data = df_label,
        aes(
          y = y,
          label = label,
          weight = NULL
        ),
        hjust = -.1
      ) +
      scale_y_continuous(limits = c(NA, 1))