Making an Animated Contour Plot
By Jake Thompson in R
September 5, 2018
Earlier this week Mike Bostock tweeted a interesting looking contour plot with a link to edit the formula and manipulate the graphic using D3.js.
A live 2D function plot. Edit the function and reply with interesting images! https://t.co/joWfPgUAAU pic.twitter.com/XYhvJBavRM— Mike Bostock (@mbostock) September 3, 2018
I decided I would attempt to recreate the image using ggplot2, and animate it using the new gganimate package.
Creating the data
I started by creating a data frame with all the combinations of
y on a grid between -10 and 10, in intervals of 0.1. Then I defined a third variable,
z as a function of
y, using the same equation as originally used
plot_data <- crossing(x = seq(-10, 10, 0.1), y = seq(-10, 10, 0.1)) %>% mutate(z = sin(sin(x * (sin(y) - cos(x)))) - cos(cos(y * (cos(x) - sin(y))))) plot_data #> # A tibble: 40,401 × 3 #> x y z #> <dbl> <dbl> <dbl> #> 1 -10 -10 -1.77 #> 2 -10 -9.9 -0.950 #> 3 -10 -9.8 -0.275 #> 4 -10 -9.7 -0.138 #> 5 -10 -9.6 0.0279 #> 6 -10 -9.5 -1.01 #> 7 -10 -9.4 -1.80 #> 8 -10 -9.3 -1.28 #> 9 -10 -9.2 -0.564 #> 10 -10 -9.1 -0.227 #> # … with 40,391 more rows
Now we can use that data to make the image!
Create the plot
To create the plot, we are basically making a heat map of sorts, where the fill is defined by the newly calculated
z variable. I use
geom_raster because it offers speed improvements over
geom_tile when all tiles are the szme size, which they are in this case. Given that we have a lot of tiles, I’ll take the speed!
ggplot(plot_data, aes(x = x, y = y)) + geom_raster(aes(fill = z), interpolate = TRUE, show.legend = FALSE) + scale_fill_viridis_c(option = "C") + coord_equal() + theme_void()
Pretty good! But then, Jeff Baumes upped the stakes!
A version enabling "t" in the equation that changes over time for animated plots.https://t.co/atNnAXwhFm pic.twitter.com/mEpbJDTQ8N— Jeff Baumes (@jeffbaumes) September 4, 2018
Well now I can’t not animate it. So…
Add time and animate
To create an animated plot, we need a time variable,
t. We start by creating a data frame similar to the one we created above. It includes all combinations of
y from -10 to 10 in increments of 0.1, but now those values are also cross with the time variable,
t, which goes from 0 to 5 in increments of 0.1. The
z variable, which will still be our fill color, is now a function of
t, as in
Jeff’s example. Finally, we can use the same ggplot2 code as above, with the addition of the
transition_time function from gganimate.
t_lookup = data_frame(t = c(seq(0, 5, 0.1), seq(4.9, 0, -0.1)), t2 = seq(0, 10, 0.1)) plot_data <- crossing(x = seq(-10, 10, 0.1), y = seq(-10, 10, 0.1), t2 = seq(0, 10, 0.1)) %>% left_join(t_lookup, by = "t2") %>% mutate(z = sin(sin(x * (sin(y + t) - cos(x - t)))) - cos(cos(y * (cos(x - t) - sin(y + t))))) ggplot(plot_data, aes(x = x, y = y)) + geom_raster(aes(fill = z), interpolate = TRUE, show.legend = FALSE) + scale_fill_viridis_c(option = "C") + coord_equal() + theme_void() + transition_time(t2)
And there we have it! With just one extra variable and one additional line of code for our plot, we have an animated contour plot!
- Posted on:
- September 5, 2018
- 3 minute read, 571 words