rggplot2gganimateaesthetics

How do I access data of ggplot2 / gganimate object from ggplot geom_* or arrange data in reliable order


I have an issue with ordering of rows from source filtered dataset used for building gganimate object.

Here is the sample data:

df <- data.frame(grName = c("red", "red", "orange", "yellow", "green", "green", "orange", 
                            "cyan", "blue", "blue", "violet", "yellow", "cyan", "violet") %>% as.factor, 
                 grVal = c(1, 3, 2, 7, 4, 5, 3.5, 
                           2, 4, 1, 6, 5, 6, 3), 
                 grFill = c("red", "red", "orange", "yellow", "green", "green", "orange", 
                            "cyan", "blue", "blue", "violet", "yellow", "cyan", "violet"),
                 frame = c(1,2,1,1,1,2,2,1,1,2,1,2,2,2)
)

When I build separate gglots for each frame value everything is OK:

ggplot(df)+
  geom_bar(aes(grName, grVal), 
           data = function(x) filter(x, grVal < 7, frame == 2),
           fill = filter(df, grVal < 7, frame == 2) %>%  pull(grFill),
           stat = "identity")

I am getting the desired frame:

enter image description here

But as soon as I apply gganimate the columns order and fill get confused:

ggplot(df)+
  geom_bar(aes(grName, grVal), 
           data = function(x) filter(x, grVal < 7),
           fill = filter(df, grVal < 7) %>%  pull(grFill),
           stat = "identity") +
  labs(title = 'Frame: {current_frame }') +
  transition_manual(frame) +
  ease_aes('linear')

Resulting gif

When I was investigating into the issue I got an alike problem when plotting a facetted ggplot:

ggplot(df)+
  geom_bar(aes(grName, grVal), 
           data = function(x) filter(x, grVal < 7),
           #fill  = ddd %>% pull(grFill),
           fill = filter(df, grVal < 7) %>%  pull(grFill),
           stat = "identity") +
  facet_grid(rows = vars(frame))

enter image description here

I would prefer not setting fill/color etc from inside aes() along with scale_*_manual() as in my real graph doing so resulted into another issue (uncontrollable hjust of colored letters) correctly plotted when color aes() is not set and color is not set at all or set by color property of geom_text(). If I have time I'll report the issue separately.

I also have to apply filters to data objects inside constructed geoms as different (many various) geoms are plotted simultaneously in my real graph and I would like to keep reverse compatibility of new animated plot with previously used static one.

I assume that getting access to data object of ggplot() when setting color (outside aes()) like I do with geoms' data: data = function(x) filter(x, ... will help me to preserve order of rows from dataset filtered/grouped by gganimate engine and thus keep order and fill / color etc. of plotted elements.

So I would appreciate any suggestions as I am stuck with the issue.


Solution

  • This is a nice example why in general I recommend to not pass vectors to arguments of the geom. Instead you could map on the fill aesthetic and use scale_fill_identity.

    library(dplyr, warn=FALSE)
    library(ggplot2)
    library(gganimate)
    
    gg <- ggplot(df) +
      geom_col(aes(grName, grVal, fill = grName),
        data = ~ filter(.x, grVal < 7),
      ) +
      scale_fill_identity()
    gg +
      labs(title = 'Frame: {current_frame }') +
      transition_manual(frame) +
      ease_aes('linear')
    #> nframes and fps adjusted to match transition
    

    And this also fixes the assignment of the colors when facetting by frame:

    gg +
      facet_wrap(~frame, ncol = 1)