How does gganimate order an ordered bar time-series?

前端 未结 2 1379
孤城傲影
孤城傲影 2020-12-14 12:17

I have a time-series of data, where I\'m plotting diagnosis rates for a disease on the y-axis DIAG_RATE_65_PLUS, and geographical groups for comparison on the x

2条回答
  •  温柔的废话
    2020-12-14 13:16

    The bar ordering is done by ggplot and is not affected by gganimate. The bars are being ordered based on the sum of DIAG_RATE_65_PLUS within each ACH_DATEyearmon. Below I'll show how the bars are ordered and then provide code for creating the animated plot with the desired sorting from low to high in each frame.

    To see how the bars are ordered, first let's create some fake data:

    library(tidyverse)
    library(gganimate)
    theme_set(theme_classic())
    
    # Fake data
    dates = paste(rep(month.abb, each=10), 2017)
    
    set.seed(2)
    df = data.frame(NAME=c(replicate(12, sample(LETTERS[1:10]))),
                    ACH_DATEyearmon=factor(dates, levels=unique(dates)),
                    DIAG_RATE_65_PLUS=c(replicate(12, rnorm(10, 30, 5))))
    

    Now let's make a single bar plot. The bars are the sum of DIAG_RATE_65_PLUS for each NAME. Note the order of the x-axis NAME values:

    df %>% 
      ggplot(aes(reorder(NAME, DIAG_RATE_65_PLUS), DIAG_RATE_65_PLUS)) +
      geom_bar(stat = "identity", alpha = 0.66) +
      labs(title='{closest_state}') +
      theme(plot.title = element_text(hjust = 1, size = 22)) 
    

    You can see below that the ordering is the same when we explicitly sum DIAG_RATE_65_PLUS by NAME and sort by the sum:

    df %>% group_by(NAME) %>% 
      summarise(DIAG_RATE_65_PLUS = sum(DIAG_RATE_65_PLUS)) %>% 
      arrange(DIAG_RATE_65_PLUS)
    
       NAME DIAG_RATE_65_PLUS
    1     A          336.1271
    2     H          345.2369
    3     B          346.7151
    4     I          350.1480
    5     E          356.4333
    6     C          367.4768
    7     D          368.2225
    8     F          368.3765
    9     J          368.9655
    10    G          387.1523
    

    Now we want to create an animation that sorts NAME by DIAG_RATE_65_PLUS separately for each ACH_DATEyearmon. To do this, let's first generate a new column called order that sets the ordering we want:

    df = df %>% 
      arrange(ACH_DATEyearmon, DIAG_RATE_65_PLUS) %>% 
      mutate(order = 1:n())
    

    Now we create the animation. transition_states generates the frames for each ACH_DATEyearmon. view_follow(fixed_y=TRUE)shows x-values only for the current ACH_DATEyearmon and maintains the same y-axis range for all frames.

    Note that we use order as the x variable, but then we run scale_x_continuous to change the x-labels to be the NAME values. I've included these labels in the plot so you can see that they change with each ACH_DATEyearmon, but you can of course remove them in your actual plot as you did in your example.

    p = df %>% 
      ggplot(aes(order, DIAG_RATE_65_PLUS)) +
        geom_bar(stat = "identity", alpha = 0.66) +
        labs(title='{closest_state}') +
        theme(plot.title = element_text(hjust = 1, size = 22)) +
        scale_x_continuous(breaks=df$order, labels=df$NAME) +
        transition_states(ACH_DATEyearmon, transition_length = 1, state_length = 50) +
        view_follow(fixed_y=TRUE) +
        ease_aes('linear')
    
    animate(p, nframes=60)
    
    anim_save("test.gif")
    

    If you turn off view_follow(), you can see what the "whole" plot looks like (and you can, of course, see the full, non-animated plot by stopping the code before the transition_states line).

    p = df %>% 
      ggplot(aes(order, DIAG_RATE_65_PLUS)) +
        geom_bar(stat = "identity", alpha = 0.66) +
        labs(title='{closest_state}') +
        theme(plot.title = element_text(hjust = 1, size = 22)) +
        scale_x_continuous(breaks=df$order, labels=df$NAME) +
        transition_states(ACH_DATEyearmon, transition_length = 1, state_length = 50) +
        #view_follow(fixed_y=TRUE) +
        ease_aes('linear')
    

    UPDATE: To answer your questions...

    To order by a given month's values, turn the data into a factor with the levels ordered by that month. To plot a rotated graph, instead of coord_flip, we'll use geom_barh (horizontal bar plot) from the ggstance package. Note that we have to switch the y's and x's in aes and view_follow() and that the order of the y-axis NAME values is now constant:

    library(ggstance)
    
    # Set NAME order based on August 2017 values
    df = df %>% 
      arrange(DIAG_RATE_65_PLUS) %>% 
      mutate(NAME = factor(NAME, levels=unique(NAME[ACH_DATEyearmon=="Aug 2017"])))
    
    p = df %>% 
      ggplot(aes(y=NAME, x=DIAG_RATE_65_PLUS)) +
      geom_barh(stat = "identity", alpha = 0.66) +
      labs(title='{closest_state}') +
      theme(plot.title = element_text(hjust = 1, size = 22)) +
      transition_states(ACH_DATEyearmon, transition_length = 1, state_length = 50) +
      view_follow(fixed_x=TRUE) +
      ease_aes('linear')
    
    animate(p, nframes=60)
    anim_save("test3.gif")
    

    For smooth transitions, it seems like @JonSpring's answer handles that well.

提交回复
热议问题