rggplot2gganimate

gganimte how to make geom_col to appear one at a time


I have a simple bar chart that I am trying to animate. I am not far from the intended result but I am out of ideas now.

I want to reveal one bar at a time and keep the previous ones using geom_col().

Here a sample df:

fr<- structure(list(HOME = c("Verona", "Atalanta", "Genk"), AWAY = c("Inter", 
"Fiorentina", "Gent"), Domicile = c(5.7034e-08, 0.9916, 0.37966
), Nul = c(0.0015489, 0.0083796, 0.39583), Défaite = c(0.99845, 
2.0228e-05, 0.22451)), row.names = c(NA, -3L), class = c("tbl_df", 
"tbl", "data.frame"))

My first intention is to add a "state-zero" of initiation for each group, so I change my df as such:

fr<-fr %>%  
  slice(1:nrow(.), row_number()) %>%
  mutate(across(c((ncol(.)-3+1):(ncol(.))),~base::as.numeric(ifelse(row_number() <= 0.5*n(), 0, .x)))) %>% 
  group_by(HOME,AWAY) %>% 
  mutate(ID = seq_along(HOME)) %>%
  pivot_longer(
               cols=c(Domicile, Nul, Défaite),
               values_to="prob",
               names_to = "type") %>% 
  mutate(game=paste0(HOME," - ", AWAY))
fr$type <-factor(fr$type,levels=c("Domicile","Nul","Défaite"))

and then I plot as follows:

f <- ggplot(fr, aes(x = type, y = prob))
f<-f+geom_col(aes(fill = type,group=game)) +
  geom_text(aes(label=ifelse(prob > 0,sprintf("%0.2f%%",prob*100),""),
                y=prob+0.15,color=type),fontface = "bold",family="Oswald", size=4)+
  facet_wrap(~game, scale="fixed",axes = "all_x",as.table = TRUE)+
  labs(subtitle = "Estimations après simulation de Monte Carlo sur un paramètre interne")+
  transition_states(type, wrap=FALSE)+
  enter_grow()+
  shadow_mark(past = TRUE)
f

However, two problems arise. First I have the fist bar corresponding to domicile which is already present meaning we don't see it grow. And finally, bars don't grow from the bottom and the percentage text label doesn't grow with the bar height but instead is already at its final value.

Here is the output I have managed to get so far: enter image description here

Thank you for your help.

**EDIT 10/09/24: **

In the meantime I found a solution that is I believe the correct way around this issue. The first part is actually achieving the same result as @stefan's reply.

So first I added a frame variable to match the transition_states() I wanted by doing the following:

fr<- fr%>% 
  mutate(type=factor(type,levels=c("Domicile","Nul","Défaite"))) %>% 
  ungroup() %>% 
  arrange(ID, type) %>% 
  mutate(frame2 = letters[rep(1:nrow(.), each = 3, length.out = nrow(.))]) %>% 
  mutate(prob = round(prob, 4)) 

Then on the graph side, I transition using the new frame2 variable, and to have the geom_text() follow the bar height, I found out we could add an enter_fly parameter that does the job just fine. I've also added some modification on the scaly_y_continous.


f <- ggplot(fr, aes(x = type, y = prob,fill = type, group = type))
f<-f+geom_col() +
  geom_text(aes(label = ifelse(prob > 0,paste0(" ",
                                             scales::percent(prob, accuracy = 0.01)),""),
                color = type),fontface = "bold",family = "Oswald", size = 4,vjust = -1)+
  scale_y_continuous(labels = scales::percent_format(accuracy = 1),expand = expansion(add = c(0,0.05)))+
  facet_wrap(~game, scale = "fixed",axes = "all_x",as.table = TRUE)+
  labs(subtitle = "Estimations après simulation de Monte Carlo sur un paramètre interne")+
  transition_states(frame2, wrap = FALSE)+
  shadow_mark(past = TRUE)+
  enter_fly(y_loc = 0)
f

And here the final result: enter image description here


Solution

  • This is only a partial solution as it fixes or achieves only two of your desired outcomes. First, I add a fake first category or level to your factor type to grow the bar for "Domicile". Second, to grow the bars from zero map type on the group aes. Additionally I set the group aes globally so that it applies to the geom_text too. As a result, the labels now also grow in line with the bars but the growing is applied to size instead of y. Hence, I call my solution only partial. Unfortunately I wasn't able to find an option to fix that.

    library(gganimate)
    library(ggplot2)
    library(dplyr, warn = FALSE)
    
    # Add a fake first category to grow the bar for "Domicile"
    fr$type <- factor(fr$type, levels = c("FOO", "Domicile", "Nul", "Défaite"))
    
    # 1. group by type to grow the bars from zero
    # 2. Set group globally for all geoms
    ggplot(fr, aes(x = type, y = prob, group = type)) +
      geom_col(aes(fill = type)) +
      geom_text(aes(
        label = ifelse(prob > 0, sprintf("%0.2f%%", prob * 100), ""),
        y = prob + 0.15, color = type
      ), fontface = "bold", family = "sans") +
      facet_wrap(~game, scale = "fixed", axes = "all_x", as.table = TRUE) +
      labs(subtitle = "Estimations après simulation de Monte Carlo sur un paramètre interne") +
      transition_states(type, wrap = FALSE) +
      enter_grow() +
      shadow_mark(past = TRUE)