Search code examples
rggplot2reversegeom-textstacked

Stacked bar chart labels change when factor removed (ggplot2)


I'm using position_stack(reverse = TRUE) for both the geom_bar and geom_text below to align the text with the correct group. It works fine when all factors are included but, when at least one factor is removed, the labels change position (and the plot also changes in width?)


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

dat <- data.frame(
  group = factor(
    c("A", "B", "C"),
    levels = c("A", "B", "C"),
    ordered = TRUE
  ),
  value = c(0.7, 0.2, 0.1))

dat %>%
  ggplot(aes(x = 1, y = value, fill = group)) +
  geom_bar(
    stat = "identity",
    position = position_stack(reverse = TRUE),
    width = 0.6,
    show.legend = FALSE
  ) +
  theme_void() +
  geom_label(
    aes(
      x = 0.65,
      y = value,
      label = percent(value, 1),
      fontface = "bold"
    ),
    position = position_stack(reverse = TRUE),
    vjust = 1.5,
    size = 5,
    colour = "white",
    show.legend = FALSE
  )


dat %>%
  filter(group %in% c("B", "C")) %>%
  ggplot(aes(x = 1, y = value, fill = group)) +
  geom_bar(
    stat = "identity",
    position = position_stack(reverse = TRUE),
    width = 0.6,
    show.legend = FALSE
  ) +
  theme_void() +
  geom_label(
    aes(
      x = 0.65,
      y = value,
      label = percent(value, 1),
      fontface = "bold"
    ),
    position = position_stack(reverse = TRUE),
    vjust = 1.5,
    size = 5,
    colour = "white",
    show.legend = FALSE
  )


sessionInfo()
#> R version 3.6.1 (2019-07-05)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 18362)
#> 
#> Matrix products: default
#> 
#> locale:
#> [1] LC_COLLATE=English_New Zealand.1252  LC_CTYPE=English_New Zealand.1252   
#> [3] LC_MONETARY=English_New Zealand.1252 LC_NUMERIC=C                        
#> [5] LC_TIME=English_New Zealand.1252    
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] scales_1.1.0  dplyr_0.8.5   ggplot2_3.3.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] Rcpp_1.0.4.6      knitr_1.28        magrittr_1.5      tidyselect_1.0.0 
#>  [5] munsell_0.5.0     viridisLite_0.3.0 colorspace_1.4-1  R6_2.4.1         
#>  [9] rlang_0.4.5       stringr_1.4.0     highr_0.8         tools_3.6.1      
#> [13] grid_3.6.1        gtable_0.3.0      xfun_0.13         withr_2.2.0      
#> [17] htmltools_0.4.0   ellipsis_0.3.0    assertthat_0.2.1  yaml_2.2.1       
#> [21] digest_0.6.25     tibble_3.0.1      lifecycle_0.2.0   crayon_1.3.4     
#> [25] farver_2.0.3      purrr_0.3.4       vctrs_0.2.4       glue_1.4.0       
#> [29] evaluate_0.14     rmarkdown_2.1     labeling_0.3      stringi_1.4.6    
#> [33] compiler_3.6.1    pillar_1.4.3      pkgconfig_2.0.3

Created on 2020-04-30 by the reprex package (v0.3.0)


Solution

  • This actually has nothing to do with your factor dropping. ggplot2 doesn't let you use position stack and nudge (which is what you'd ideally like). You can plot the graph and then adjust it afterwards and re-plot.

    library(ggplot2)
    library(dplyr)
    library(scales)
    library(grid)
    
    dat <- data.frame(
      group = factor(
        c("A", "B", "C"),
        levels = c("A", "B", "C"),
        ordered = TRUE
      ),
      value = c(0.7, 0.2, 0.1))
    
    
    p1 <- dat %>%
      ggplot(aes(x = 1, y = value, fill = reorder(group, value))) +
      geom_bar(
        stat = "identity",
        position = "stack",
        show.legend = FALSE
      ) +
      theme_void() +
      geom_label(
        aes(
          y = value,
          label = percent(value, 1),
          fontface = "bold"
        ),
        position = position_stack(vjust = .5),
        size = 5,
        colour = "white",
        show.legend = FALSE
      )+
      xlim(c(.5,1.5))
    
    p1 <- ggplot_build(p1)
    
    #change label position
    p1$data[[2]]$x <- p1$data[[2]]$x - .5
    #rebuild graph
    grid.draw(ggplot_gtable(p1))
    dev.off()
    

    enter image description here

    p2 <- dat %>%
      filter(group %in% c("B", "C")) %>%
      ggplot(aes(x = 1, y = value, fill = reorder(group, value))) +
      geom_bar(
        stat = "identity",
        position = "stack",
        show.legend = FALSE
      ) +
      theme_void() +
      geom_label(
        aes(
          y = value,
          label = percent(value, 1),
          fontface = "bold"
        ),
        position = position_stack(vjust = .5),
        size = 5,
        colour = "white",
        show.legend = FALSE
      )+
      xlim(c(.5,1.5))
    
    
    p2 <- ggplot_build(p2)
    
    #change label position
    p2$data[[2]]$x <- p2$data[[2]]$x - .5
    
    #rebuild graph
    grid.draw(ggplot_gtable(p2))
    #clear graph
    dev.off()
    

    enter image description here