Search code examples
rggplot2gganimate

How to gganimate a stacked bar graph?


I am trying to make a transition between four stacked bar graphs. The output is not quite what I expected and I haven't been able to figure if it's an error in my code or if it's a bug in gganimate R package.

This is the data frame I use:

df <- structure(list(name = c("variable", "variable", "variable", "variable",    
    "variable", "variable", "variable", "variable", "variable", "variable", 
    "variable", "variable", "variable"), groups = structure(c(3L, 
    3L, 3L, 3L, 2L, 2L, 2L, 1L, 1L, 1L, 4L, 4L, 4L), .Label = c("group 1", 
    "group 2", "group 3", "group 4"), class = "factor"), score = structure(c(4L, 
    3L, 2L, 1L, 3L, 2L, 1L, 3L, 2L, 1L, 3L, 2L, 1L), .Label = c("4", 
    "3", "2", "1"), class = c("ordered", "factor")), percentage = c(8, 
    38, 38, 16, 17.1428571428571, 40, 42.8571428571429, 40, 20, 40, 
    5, 65, 30), percentage2 = c("8%", "38%", "38%", "16%", "17.1%", 
    "40%", "42.9%", "40%", "20%", "40%", "5%", "65%", "30%"), label = c(0.04, 
    0.27, 0.65, 0.92, 0.0857142857142857, 0.371428571428571, 0.785714285714286, 
    0.2, 0.5, 0.8, 0.025, 0.375, 0.85)), row.names = c(NA, -13L), class = "data.frame")

When I make a stacked bar graph of just one stage of the groups variable, I get e.g. this:

library(ggplot2)
library(dplyr)

ggplot(filter(df, groups == "group 3"),
       aes(x = name, y = percentage, fill = score)) +
    geom_bar(stat = "identity", position = "fill", width = 0.8) +
    geom_text(aes(y = label, label = percentage2), color = "grey25") +
    coord_flip() +
    scale_fill_manual(values=c("darkgreen", "lightgreen", "yellow", "red"), 
                      guide = guide_legend(reverse = TRUE), drop=FALSE) 

enter image description here

But When I try to add a gganimate animation of the four different group stages I get this:

library(gganimate)

ggplot(df, aes(x = name, y = percentage, fill = score)) +
    geom_bar(stat = "identity", position = "fill", width = 0.8) + 
    geom_text(aes(y = label, label = percentage2), color = "grey25") +
    coord_flip() +
    scale_fill_manual(values = c("darkgreen", "lightgreen", "yellow", "red"), 
                      guide= guide_legend(reverse = TRUE), drop = FALSE) + 
    transition_states(groups, transition_length = 2, state_length =  1)

enter image description here

It seems to add all the percentages (bar lengths) of all the groups to the animation at the same time. I want a transition between the stacked bar graphs of the four different groups without the gaps. How can I get this animation to make a transition between the bars without gaps?


Solution

  • 1

    Definitely possible, but in the current version of gganimate you need to edit your data frame.

    Code

    g <- ggplot(df, aes(x = name, y = c, fill = score, group = score)) +
        geom_col(position = "identity", width = 0.8) +
        coord_flip() +
        labs(title = "{closest_state}") +
        geom_label(aes(y = c, label = percentage2)) +
        scale_fill_manual(values = c("darkgreen", "lightgreen", "yellow", "red"), 
                          guide= guide_legend(reverse = TRUE), drop = FALSE) +
        transition_states(groups, transition_length = 2, state_length = 1)
    animate(g, nframes = 100)
    

    Data

    df$c <- ave(df$percentage, df$group, FUN=cumsum)
    df <- df[order(df$groups, df$score, df$c), ]
    
    df
           name  groups score percentage percentage2      label         c
    10 variable group 1     4   40.00000         40% 0.80000000 100.00000
    9  variable group 1     3   20.00000         20% 0.50000000  60.00000
    8  variable group 1     2   40.00000         40% 0.20000000  40.00000
    7  variable group 2     4   42.85714       42.9% 0.78571429 100.00000
    6  variable group 2     3   40.00000         40% 0.37142857  57.14286
    5  variable group 2     2   17.14286       17.1% 0.08571429  17.14286
    4  variable group 3     4   16.00000         16% 0.92000000 100.00000
    3  variable group 3     3   38.00000         38% 0.65000000  84.00000
    2  variable group 3     2   38.00000         38% 0.27000000  46.00000
    1  variable group 3     1    8.00000          8% 0.04000000   8.00000
    13 variable group 4     4   30.00000         30% 0.85000000 100.00000
    12 variable group 4     3   65.00000         65% 0.37500000  70.00000
    11 variable group 4     2    5.00000          5% 0.02500000   5.00000
    

    ========================

    Explanation

    Why? In gganimate version "0.9.9.9999" the animated plot will not group & stack correctly (a bug, as you pointed out correctly). This is why you need to

    1. Feature engineer the position of the bar (variable c)
    2. Order the bars by descending size of c (so that the bigger ones don't overlap the smaller ones)

    What was really helpful: to break down the code to the bare bones and just keep the important stuff:

    g <- ggplot(df, aes(x = "", y = c, fill = score, group = score)) +
        geom_col(position = "identity") +
        labs(title = "{closest_state}") +
        transition_states(groups, transition_length = 2, state_length = 1)
        animate(g, nframes = 10)
    

    This was much easier to work with than the original code. There it became clear that the problem lies somewhere in the interplay of y = (e.g., c, percentage), group = (e.g., score, groups), and position = (e.g., stack, dodge, dodge2, identity, fill).

    If you have any questions please do not hesitate to shoot me a message.