Programmatically set "midpoint" for scale_*_gradient2()


I'm not sure if this is more of a gganimate question or a ggplot2 question -- so I tagged them both :wink: :

Anyway, in scales_color_gradient2(), which (by default) sets red to the low points, blue to the high points, and white to the midpoints, the midpoint = argument is defaulted to 0.

In all examples I found only, when people wanted to change the midpoint to the median of the data, they did something where they'd set the midpoint outside of the ggplot call, and use it as a variable.

Is there a way to do this automatically?

My motivation is in gganimate. Over the different frames, the midpoint changes. In the example below, we can see that by the end, all points are blue, since all data is above the global median by then. But I'd love to set it, such that within each frame, the midpoint is set to the median.

Is this possible?


df1 <- tibble(id0 = 1:1000,
              x0 = runif(1000, 10, 20),
              y0 = runif(1000, 10, 20)) %>%
  crossing(year0 = 2000:2019) %>%
  mutate(z0 = 1.3 * x0 + 0.7 * y0 + rnorm(nrow(.), 0, 5),
         z1 = 1.3 * x0 + 0.7 * y0 + 0.8 * (year0 - 2000) + rnorm(nrow(.), 0, 5))

mid0 <- median(df1$z0)
gg0 <- df1 %>%
  ggplot(aes(x0, y0, color = z0)) +
  geom_jitter(width = 0.5, height = 0.5) +
  theme_light() +
  scale_color_gradient2(midpoint = mid0)

mid1 <- median(df1$z1)
gg1 <- df1 %>%
  ggplot(aes(x0, y0, color = z1)) +
  geom_point(size = 4.5) +
  theme_light() +
  scale_color_gradient2(midpoint = mid0) +
  transition_time(year0) +
  labs(title = "Year: {frame_time}")
animate(gg1, nframes = 20, fps = 1)

Created on 2019-08-27 by the reprex package (v0.3.0)

I'm not sure how to do this with gganimate or if it's possible. Until someone comes along with a gganimate answer, I've provided a different approach below. Question: If you keep changing the median to be the median for the current year, you'll have pretty much the same color distribution in every frame, even though the data are changing. It seems like that would be confusing and defeat the purpose of mapping numbers to colors.


rng = c(floor(min(df1$z1)/10)*10, ceiling(max(df1$z1)/10)*10)

  for (i in unique(df1$year0)) {
    dat = df1 %>% filter(year0==i)
    mid1 <- median(dat$z1)
    gg1 <- dat %>%
      ggplot(aes(x0, y0, color = z1)) +
      geom_point(size = 4.5) +
      theme_light() +
      scale_color_gradient2(midpoint = mid1, breaks=seq(rng[1],rng[2],10), limits=rng) +
      labs(title = paste("Year:", i)) 


Thanks - I didn't know about saveGIF! I'll wait a few days to see if someone has a more direct answer, but I worry that its not actually possible.

And yes, I recognize that the final gif here is boring - I didn't feel like developing a reprex where it was more sensible :laughing:

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.