Search code examples
ranimationggplot2data-visualizationgganimate

gganimate: How to make stacked bar chart grow smoothly upwards from x-axis


I'm having trouble animating a stacked static plot. (See animation below)

The animation moves in a volatile way from left to right, instead of each bar growing upwards from the x-axis. The stacked parts also don't grow very smoothly together, which I can't seem to solve.

What do I need to change in my code, so that, as the animated plot transitions along the x-axis, the individual bars grow upwards? I would also like to have the stacked bars grow more smoothly, so the stacked bar parts aren't "thrown" on top of each other.

How is that possible?

The top gif in this article is what I'm looking for, but I don't understand how and where his code differs from mine: ideal animation

Here is my reprex:

#Static df
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
year <- as.numeric(c(1996:2015,
                       1996:2015,
                       1996:2015))
c <- c(39, 40, 67, 80, 30, 140, 90, 23, 100, 123,
       140, 160, 100, 89, 173, 200, 32, 90, 100, 211,
       1, 2, 1, 13, 3, 3, 30, 1, 3, 3,
       1, 1, 20, 2, 1, 10, 1, 2, 1, 1,
       1, 3, 1, 2, 1, 3, 1, 3, 6, 1,
       1, 30, 1, 1, 8, 9, 1, 32, 1, 1)
cat <- as.character(c("out", "out", "out", "out", "out", "out", "out", "out", "out", "out",
                   "out", "out", "out", "out", "out", "out", "out", "out", "out", "out",
                   "in", "in", "in", "in", "in", "out", "in", "in", "in", "in",
                   "in", "in", "in", "in", "in", "in", "in", "in", "in", "in",
                   "other", "other", "other", "other", "other", "other", "other", "other", "other", "other",
                   "other", "other", "other", "other", "other", "other", "other", "other", "other", "other"))
cat <- as.factor(cat)
static_df_test <- data.frame(year, c, cat) %>%
  select(year, cat, c) %>%
  arrange(year, cat)


#Static plot
library(ggplot2)
(static_plot <- ggplot(static_df_test) +
    geom_bar(data = static_df_test, stat="identity", position ="stack",
             aes(x = year, y = c, fill = cat)) +
    scale_x_continuous(breaks = seq(1996, 2015, 1),
                       expand = c(0.003, 0.003),
                       labels = c(1996, 97, 98, 99, 2000,
                                  "01", "02", "03", "04", "05", "06",
                                  "07", "08", "09", 10, 11, 12, 13, 14, 15)) +
    scale_y_continuous(breaks = seq(0, 250, 25),
                       expand = c(0,0),
                       limits = c(0,250)) +
    labs(x = "year", y = "c")
)

#Animated plot
library(gganimate)
library(ggplot2)
ani <- (static_plot) +
  transition_time(as.integer(year)) +
  enter_grow() + shadow_mark(past = TRUE)

animate(ani, fps = 8, 50, duration = 15,
        width = 1500, height = 750, renderer = gifski_renderer())

Right now, the bars transition by moving rightwards off of the previous bar. See the current animation here: Current animation

Thank you in advance for any help!


Solution

  • There is likely a more correct way to do this, but it can be accomplished by changing the way you construct your initial static plot and using transition_layers() and enter_drift().

    Note that to simplify my example I am only including years 1996-2000. To animate all years simply copy and paste the geom_bar() sections and change the year being filtered.

    #Static plot
    library(ggplot2)
    (static_plot <- ggplot(static_df_test) +
        geom_bar(data = static_df_test %>% filter(year == 1996), stat="identity", position ="stack",
                 aes(x = year, y = c, fill = cat)) +
        geom_bar(data = static_df_test %>% filter(year == 1997), stat="identity", position ="stack",
                 aes(x = year, y = c, fill = cat)) +
        geom_bar(data = static_df_test %>% filter(year == 1998), stat="identity", position ="stack",
                 aes(x = year, y = c, fill = cat)) +
        geom_bar(data = static_df_test %>% filter(year == 1999), stat="identity", position ="stack",
                 aes(x = year, y = c, fill = cat)) +
        geom_bar(data = static_df_test %>% filter(year == 2000), stat="identity", position ="stack",
                 aes(x = year, y = c, fill = cat)) +
        scale_x_continuous(breaks = seq(1996, 2015, 1),
                           expand = c(0.003, 0.003),
                           labels = c(1996, 97, 98, 99, 2000,
                                      "01", "02", "03", "04", "05", "06",
                                      "07", "08", "09", 10, 11, 12, 13, 14, 15)) +
        scale_y_continuous(breaks = seq(0, 250, 25),
                           expand = c(0,0),
                           limits = c(0,250)) +
        labs(x = "year", y = "c")
    )
    
    #Animated plot
    library(gganimate)
    library(ggplot2)
    
    ani <- (static_plot) +
      transition_layers(layer_length = 1, transition_length = 2) +
      enter_drift(x_mod = 0, y_mod = -max(static_df_test$c))
    
    animate(ani, fps = 10, duration = 10,
            width = 600, height = 500, renderer = gifski_renderer())
    

    By placing each year-based bar on its own layer, we can use transition_layers() to make them appear one by one while keeping all old layers. We use enter_drift() to animate each bar as if it is drifting upwards from below the graph.

    The animated output is here: link