Open ptoche opened 6 years ago
Example 1:
Objective: reveal a single point sequentially: reveal (x_{i}, y_{i})
+ hide (x_{i-1}, y_{i-1})
library(reprex)
# Packages + Data
library(ggplot2)
library(gganimate)
library(tibble)
df <- tibble(x = -10:10, y = abs(x))
# Plots
p1 <- ggplot(data = df, aes(x, y)) +
geom_point(size = 5) +
transition_manual(x) +
labs(title = "transition_manual(x)")
p1 <- animate(p1)
#> nframes and fps adjusted to match transition
p2 <- ggplot(data = df, aes(x, y)) +
geom_point(size = 5) +
transition_manual(y) +
labs(title = "transition_manual(y)")
p2 <- animate(p2)
#> nframes and fps adjusted to match transition
combine_gifs(plot1 = p1, plot2 = p2)
#> Loading required package: magick
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
Created on 2018-12-11 by the reprex package (v0.2.1)
Verdict: Success.
Comment: To reveal the points in a vertical direction, use transition_manual(y)
. Very intuitive.
Following on from the previous examples, below are attempts to reveal points sequentially while keeping them on the graph. Thanks Thomas for showing me how to achieve this objective.
Example 2:
Objective: reveal all points sequentially: reveal (x_{i}, y_{i})
without hiding (x_{i-1}, y_{i-1})
# Packages + Data
library(reprex)
library(ggplot2)
library(gganimate)
library(tibble)
df <- tibble(x = -10:10, y = abs(x))
# Plots
p1 <- ggplot(data = df, aes(x, y, group = seq_along(x))) +
geom_point(size = 5) +
transition_reveal(x) +
labs(title = "transition_reveal(x)")
p1 <- animate(p1)
p2 <- ggplot(data = df, aes(x, y, group = seq_along(x))) +
geom_point(size = 5) +
transition_reveal(y) +
labs(title = "transition_reveal(y)")
p2 <- animate(p2)
combine_gifs(plot1 = p1, plot2 = p2)
#> Loading required package: magick
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
anim_save("transition_reveal.gif")
Created on 2018-12-11 by the reprex package (v0.2.1)
Verdict: Success.
Comment: You need group = seq_along(x)
in the aes
and transition_reveal()
. This will take some getting used to.
The default nframes
is 100. This seems like an excessive number of frames for an animation that would be expected to have 21 frames. I wonder if the gifs above can be created with 21 frames by default or if one is expected to override the default manually... Will look into this.
length(-10:10)
## 21
This is only possible for some transitions, e.g. transition_manual... In your case the only reason why it can be done with 21 frames is because there is only one layer and each time point fed into transition_reveal is distributed evenly across the range and no point has the same group aesthetic. I'm not going to create special cases for such things as it is likely to break all the time
also, sorry for breaking all your code, but breaking changes to transition_reveal has just been merged in, removing the id
argument. You should now use the group
aesthetic in each layer to set which elements belongs together
Thanks Thomas. Will look into this. I'm only just learning the new api, so I don't mind breaking changes.
Is there a way to override nframes
from inside, say, transition_manual()
? How about fps
? Would it be too much work to allow the user to override them? I saw in the code that nframes
is set to 100
by default inside create_scene
(but if I understand correctly the number of frames also appears as the product of total
and detail
, set by default to the product of 100
and 1
), but I didn't see if it can be accessed.
I haven't updated to the latest version, so let me do that first. But when I tried to set the nframes
implicitly by giving the frames
option the x
vector, the result was that the animation lost its cumulative = TRUE
effect (sorry for using old terminology here). Will check the latest update and explore further, but adding transition_manual(frames = x)
correctly resulted in 21 frames being produced (instead of 100), but it also removed the "cumulative" effect. As I'm super new, no idea if what I write even makes sense.
ggplot(data = df, aes(x, y)) +
geom_point(size = 5) +
transition_reveal(x, x) +
transition_manual(frames = x)
The number of frames is not really something the transitions should worry about unless the data provided does not support the number of frames requested. The idea is that an animation specification is dimensionless and only upon requesting a render is anything determined. It is thus the responsibility of the animate()
function to request a number of frames along with a framerate. animate
is called implicitly upon printing the animation object, using the defaults (which is 100 frames and 10 fps), but can either be called explicitly with other values or the defaults can be overwritten - see ?animate
for more details
I follow up with a few more attempts at making introductory examples. I'll update the code as I make progress. Comments welcome.
Example 3:
Objective: reveal a single line segment sequentially: show (x_{i}, y_{i}) -- (x_{i-1}, y_{i-1})
+ hide (x_{i-1}, y_{i-1}) -- (x_{i-2}, y_{i-2})
Verdict: Thomas says there is no easy way to do this right now... To be continued.
Example 4:
Objective: reveal a line sequentially: reveal (x_{i}, y_{i}) -- (x_{i-1}, y_{i-1})
without hiding (x_{i-1}, y_{i-1})-(x_{i-2}, y_{i-2})
# Packages + Data
library(reprex)
library(ggplot2)
library(gganimate)
library(tibble)
df <- tibble(x = -10:10, y = abs(x))
# Plots
p1 <- ggplot(data = df, aes(x, y)) +
geom_path(size = 2) +
transition_reveal(x) +
labs(title = "geom_path() with transition_reveal(x)")
p1 <- animate(p1)
p2 <- ggplot(data = df, aes(x, y)) +
geom_path(size = 2) +
transition_reveal(y) +
labs(title = "geom_path() with transition_reveal(y)")
p2 <- animate(p2)
combine_gifs(plot1 = p1, plot2 = p2)
#> Loading required package: magick
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
Created on 2018-12-11 by the reprex package (v0.2.1)
Verdict: Success.
Comment: geom_line()
is not recommended for animations. While it works in this example, in general Thomas recommends geom_path()
in conjunction with transition_reveal()
.
Comment: In this example, transition_manual()
does not work.
Comment: Traces the path of the points over 100 frames, but this can be changed with (say) animate(p1, nframes = 10, fps = 1)
Example 4b: Things that go "wrong" when attempting to animate geom_line()
. Use geom_path()
instead.
# Packages + Data
library(reprex)
library(ggplot2)
library(gganimate)
library(tibble)
df <- tibble(x = -10:10, y = abs(x))
# Plots
p1 <- ggplot(data = df, aes(x, y)) +
geom_line(size = 2) +
transition_reveal(x) +
labs(title = "geom_line() with transition_reveal(x)")
p1 <- animate(p1)
p2 <- ggplot(data = df, aes(x, y)) +
geom_line(size = 2) +
transition_reveal(y) +
labs(title = "geom_line() with transition_reveal(y)")
p2 <- animate(p2)
combine_gifs(plot1 = p1, plot2 = p2)
#> Loading required package: magick
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
# Verdict: Not recommended.
Created on 2018-12-11 by the reprex package (v0.2.1)
In general, transition_manual should only be used if you want to forego gganimate's automatic handling altogether... using it to reveal a line is not what it does...
You should generally be hesitant to use geom_line with transition_reveal unless you reveal along the x-axis. using geom_path will give you the expected result
Thanks for the feedback Thomas. I have updated the examples above. Below an updated summary.
Example 1: Objective: reveal a single point sequentially: reveal (x_{i}, y_{i})
+ hide (x_{i-1}, y_{i-1})
. Verdict: Success.
Example 2: Objective: reveal all points sequentially: reveal (x_{i}, y_{i})
without hiding (x_{i-1}, y_{i-1})
. Verdict: Success.
Example 3: Objective: reveal a single line segment sequentially: reveal (x_{i}, y_{i}) -- (x_{i-1}, y_{i-1})
+ hide (x_{i-1}, y_{i-1}) -- (x_{i-2}, y_{i-2})
. Verdict: Failure.
Example 4: Objective: reveal a line sequentially: reveal (x_{i}, y_{i}) -- (x_{i-1}, y_{i-1})
without hiding (x_{i-1}, y_{i-1})-(x_{i-2}, y_{i-2})
. Verdict: Success.
For example 2, a simple modification is all it takes:
ggplot(data = df, aes(x, y, group = seq_along(x)) + #group just need to be unique for each point
geom_point(size = 5) +
transition_reveal(x) +
labs(title = "transition_reveal(x)")
I still don't understand what you wan to achieve in example 3
Thanks for your help Thomas. For me, with or without group = seq_along(x)
I get the same output. Let me try to clarify what I'm looking for in Example 3 and get back to you.
library(gganimate)
#> Loading required package: ggplot2
df <- data.frame(x = -10:10, y = abs(-10:10))
ggplot(data = df, aes(x, y, group = seq_along(x))) + #group just need to be unique for each point
geom_point(size = 5) +
transition_reveal(x)
Created on 2018-12-11 by the reprex package (v0.2.1)
stupid me I had the group
outside the aes()
. So sorry! And thanks again for your patience!!
Example 4 with fewer frames than default. The first 5 frames
print_frames <- function(data, nframes = NULL) {
require(ggplot2)
if (is.null(nframes)) nframes = nrow(data)
for (i in 1:nframes) {
filename <- paste0("frame-", i, ".png")
title <- paste0("Frame", i)
ggplot(data[1:i, ], aes(x, y)) +
geom_line(size = 2) +
scale_x_continuous(limits = c(min(data[1]), max(data[1]))) +
scale_y_continuous(limits = c(min(data[2]), max(data[2]))) +
labs(title = title)
ggsave(filename, width = 5, height = 5)
}
}
print_frames(data = df, nframes = 5)
That is basically example 4 with a low framerate
library(gganimate)
#> Loading required package: ggplot2
df <- data.frame(x = -10:10, y = abs(-10:10))
# Plots
p1 <- ggplot(data = df, aes(x, y)) +
geom_path(size = 2) +
transition_reveal(x) +
labs(title = "geom_path() with transition_reveal(x)")
animate(p1, nframes = 10, fps = 1)
Created on 2018-12-11 by the reprex package (v0.2.1)
Oh I understand now: you set nframes
inside the animate()
function. Excellent. Indeed I was looking for a frame rate that would match the supplied data. Thanks.
I wrote too fast, I meant this for Example 3:
print_frames <- function(data, nframes = NULL) {
require(ggplot2)
if (is.null(nframes)) nframes = nrow(data)
for (i in 1:nframes) {
filename <- paste0("frame-", i, ".png")
title <- paste("Frame", i)
ggplot(data[i:(i+1), ], aes(x, y)) +
geom_line(size = 2) +
scale_x_continuous(limits = c(min(data[1]), max(data[1]))) +
scale_y_continuous(limits = c(min(data[2]), max(data[2]))) +
labs(title = title)
ggsave(filename, width = 5, height = 5)
}
}
print_frames(data = df, nframes = 5)
there is currently no way to do that gracefully
I have updated Examples 1, 2, and 4. Feel free to put them together inside a "Getting Started" wiki, if you think they could be useful. I certainly feel much more comfortable with the package having worked through them and been able to compare different options side by side. I'll close by thanking you for a great package and your kind help!
Below an animation intended to illustrate the use of counters for transition_manual()
and transition_reveal()
. Get the current counter with print("{frame}")
. Get the total number of frames with print("{nframes}")
.
Example 5: Printing counters
library(reprex)
library(ggplot2)
library(gganimate)
# Data
df <- structure(list(x = 1:50, y = c(1.13220278129597, 1.19052387615662,
1.08245817019419, 1.092026178772, 1.10044827996026, 1.10761637669097,
1.15670117446199, 1.10614650904771, 1.10850359605767, 1.10888736691465,
1.13057762527772, 1.1097108873825, 1.10079690884314, 1.06299876187194,
1.02176309803305, 1.00468521705448, 1.01677335399114, 1.05763641886816,
1.04518028693855, 1.03553964517436, 1.05894958690745, 1.05640164807922,
1.03859260999251, 1.02183113310639, 1.02216937673504, 1.02203574505795,
1.02211730017492, 1.02587886449655, 1.02756514422318, 1.03358427512605,
1.04717537432021, 1.04464239078604, 1.05032082010357, 1.04757090239588,
1.04442488099086, 1.04616044456226, 1.0379115427868, 1.03534523721584,
1.02857437409865, 1.03808867100352, 1.0402482762807, 1.04328589395803,
1.03610034851675, 1.0311223037094, 1.02037361781279, 1.00817879202418,
1.02145919273311, 1.02037759867125, 1.01944090162799, 1.02264408869067
)), row.names = c(NA, 50L), class = "data.frame")
# cumulative = FALSE
n = nrow(df) # number of frames to match sample size
p1 <- ggplot(data = df, aes(x = x, y = y)) +
transition_manual(x) +
geom_point(size = 2, color = "darkblue") +
labs(x = "sample size",
y = "sampling mean",
title = "Number of samples = {frame}, Maximum size = {nframes}")
p1 <- animate(p1, nframes = n, fps = 1)
# cumulative = TRUE
n = nrow(df) # number of frames to match sample size
p2 <- ggplot(data = df, aes(x = x, y = y, group = seq_along(x))) +
transition_reveal(x) +
geom_point(size = 2, color = "darkblue") +
labs(x = "sample size",
y = "sampling mean",
title = "Number of samples = {frame}, Maximum size = {nframes}")
p2 <- animate(p2, nframes = n, fps = 1)
combine_gifs(plot1 = p1, plot2 = p2)
#> Loading required package: magick
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
Created on 2018-12-14 by the reprex package (v0.2.1)
Comments: 1. having to add group = seq_along()
in the aes()
is not easy to get used to. Would it be feasible to add an option inside the transition_manual(x, cumulative = TRUE)
? Or does it go too far against the "grammar"?
2. The following are ways to access frame information in different settings: {current_frame}
, {frame_along}
, {closest_state}
, {frame_time}
. Are there more? It seems like rather a lot. IMHO it would be more user-friendly to have a single universal variable for all that. It could be a short-hand, something like {frame_counter}
. I do think {nframes}
is a good, easy to remember name.
Edit: Thomas explained to me that the counter may be accessed with {frame}
. I have updated the code accordingly. Thanks Thomas!
For 1) You don't need to set grouping when using transition_manual, as there is really no tweeting happening at all
To answer 2): if you simply want a counter for which frame you're at, then the frame
variable can be used. This is simply counting the frame along the animation and is not related to the data you are displaying (other such variables are nframes
, and progress
). The reason why the other variables have different names is that they are not alike. They are not simple counters but calculated from the data as well as the specific transformations that the transition makes. Giving them the same name would imply they are alike
Thanks Thomas.
Oh I didn't realize there was a frame
variable for that. Great. I'll update the code with that information.
Do you mean there's a simpler way to obtain the "cumulative" effect?
Right now, this is my understanding:
# Animate points on/off:
ggplot(data = df, aes(x, y)) +
transition_manual(x) +
geom_point() +
# Animate points on/on
ggplot(data = df, aes(x, y, group = seq_along(x))) +
transition_reveal(x) +
geom_point()
So it's easy enough to remember this:
transition_manual(x)
transition_reveal(x)
But much harder to remember to also add group = seq_along(x)
to obtain the "cumulative" effect.
I for one am likely to remember this, now that I've talked about it so much, I just think it's a bit of a hurdle for a beginner. But perhaps I've missed an easier way to do this (that is, Example 2 above)?
There are many different ways to achieve seemingly identical results on. The different transitions differ, not in what type of output they can create, but in how they interpret the layer data and create an animation for that. In much the same way that "complaining" that you have to sort the data by x in order to make geom_path
behave like geom_line
doesn't make much sense it also doesn't make much sense to complain that transition_reveal
requires special operations to behave like transition_manual
(I'm using "complain" here in a very informal and collegial way)
I see thanks Thomas. I'm not complaining, just giving feedback about a new user's experience with the package. My intention was just to give you constructive feedback. APOLOGIES if it sounded like a complain, it wasn't.
On another matter: Example 5 crashed at around 1,000 points (two plots are created each with 1,000 points and it crashes on my 2017 MacBook Pro). It's not a complaint (!). Gifs with so many points were not such a good idea to begin with. :-)
I know, hence the final parenthesis - I used "complain" for lack of better word...
Your crash is related to the magick package when assembling the two plots. ImageMagick loads all images in to memory before assembling the gif, so at a certain point there are too many frames to handle... gifski is much better to handle this as it only have a single frame in memory at a time
Oh thanks, I'll look into gifski
!
I just saw your Getting Started
doc (https://gganimate.com/articles/gganimate.html). As I don't know how to leave messages there, I'll leave a brief comment here: Very nice!
there's a typo: the definition of fps
is the same as nframes
.
For me the most useful part of that intro was showing how to use the counters.
I'm a bit puzzled by your first example: what is the use-case for having a scatter of points move away from their (x, y)
data values to other coordinates that do not correspond to actual data? (except that it looks like a flock of birds and is pretty).
I've been a fan of
gganimate
. Great package! I am updating several animations I had made with the earlier API... I cannot seem to even get started. I have seen the wiki, but these examples are too advanced and I didn't manage to adapt them right away to my simple needs. I would like to suggest adding simpler examples to the README and/or wiki.Suggestion: (Help) Create a few simple examples for dummies:
In the
reprex
below, I suggest 4 or 5 examples that a newbie could use to get started. Please help me use the correct syntax in those cases where I haven't worked it out. Thanks!Below two basic plots to animate:
Created on 2018-12-11 by the reprex package (v0.2.1)
A convenience function to display GIFs side by side (used in the examples below).