Animate the process of adding layers to a ggplot2 plot - r

I am starting to get familiar with gganimate, but I want to extend my gifs further.
For instance, I can throw a frame on one variable in gganimate but what if I want to animate the process of adding entirely new layers/geoms/variables?
Here's a standard gganimate example:
library(tidyverse)
library(gganimate)
p <- ggplot(mtcars, aes(x = hp, y = mpg, frame = cyl)) +
geom_point()
gg_animate(p)
But what if I want the gif to animate:
# frame 1
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point()
# frame 2
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(aes(color = factor(cyl)))
# frame 3
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(aes(color = factor(cyl), size = wt))
# frame 4
ggplot(mtcars, aes(x = hp, y = mpg)) +
geom_point(aes(color = factor(cyl), size = wt)) +
labs(title = "MTCARS")
How might this be accomplished?

You can manually add a frame aesthetic to each layer, though it will include the legends for all of the frames immediately (Intentionally, I believe, to keep ratios/margins, etc. correct:
saveAnimate <-
ggplot(mtcars, aes(x = hp, y = mpg)) +
# frame 1
geom_point(aes(frame = 1)) +
# frame 2
geom_point(aes(color = factor(cyl)
, frame = 2)
) +
# frame 3
geom_point(aes(color = factor(cyl), size = wt
, frame = 3)) +
# frame 4
geom_point(aes(color = factor(cyl), size = wt
, frame = 4)) +
# I don't think I can add this one
labs(title = "MTCARS")
gg_animate(saveAnimate)
If you want to be able to add things yourself, and even see how legends, titles, etc. move things around, you may need to step back to a lower-level package, and construct the images yourself. Here, I am using the animation package which allows you to loop through a series of plots, with no limitations (they need not be related at all, so can certainly show things moving the plot area around. Note that I believe this requires ImageMagick to be installed on your computer.
p <- ggplot(mtcars, aes(x = hp, y = mpg))
toSave <- list(
p + geom_point()
, p + geom_point(aes(color = factor(cyl)))
, p + geom_point(aes(color = factor(cyl), size = wt))
, p + geom_point(aes(color = factor(cyl), size = wt)) +
labs(title = "MTCARS")
)
library(animation)
saveGIF(
{lapply(toSave, print)}
, "animationTest.gif"
)

The gganimate commands in the earlier answers are deprecated as of 2021 and won't accomplish OP's task.
Building on Mark's code, you can now simply create a static ggplot object with multiple layered geoms and then add the gganimate::transition_layers function to create an animation that transitions from layer to layer within the static plot. Tweening functions like enter_fade() and enter_grow() control how elements change into and out of frames.
library(tidyverse)
library(gganimate)
anim <- ggplot(mtcars, aes(x = hp, y = mpg)) +
# Title
labs(title = "MTCARS") +
# Frame 1
geom_point() +
# Frame 2
geom_point(aes(color = factor(cyl))) +
# Frame 3
geom_point(aes(color = factor(cyl), size = wt)) +
# gganimate functions
transition_layers() + enter_fade() + enter_grow()
# Render animation
animate(anim)

the animation package doesn't force you to specify frames in the data. See the example at the bottom of this page here, where an animation is wrapped in a big saveGIF() function. You can specify the duration of individual frames and everything.
The drawback to this is that, unlike the nice gganimate functions, the basic frame-by-frame animation wont hold the plot dimensions/legend constant. But if you can hack your way into displaying exactly what you want for each frame, the basic animation package will serve you well.

Related

How to set geom_density_2d_filled as background in gganimate

I try to use geom_density_2d_filled as background in my animation:
library(tidyverse)
library(gganimate)
mtcars_ <- mtcars
mtcars_ <- rename(mtcars_, mpg_ = mpg, disp_ = disp)
gg <- ggplot(mtcars, aes(x = mpg, y = disp)) + geom_density_2d_filled(data = mtcars_, aes(x = mpg_, y = disp_)) + geom_line() + theme(legend.position = "none")
gg
anim <- gg + transition_reveal(mpg) + shadow_wake(1)
anim
but I have no background in this case.
When I use geom_density_2d instead of geom_density_2d_filled everything is ok. What am I doing wrong?
I think transition_reveal is looking for an mpg variable for each layer.
If you add
mtcars_$mpg = min(mtcars$mpg)
it will plot with the background, but MUCH slower because that layer takes more computing per frame than the line. If that's a problem you might look at an alternative, like saving the background to an image and printing the image as a bottom layer.

Adding the same component to a list of ggplots

I have a list of ggplot2 plots, and I want to add the same title (Cars to each element of the list
library(ggplot2)
l <- list(
ggplot(data = mtcars, aes(x = mpg, y = cyl, col = am)) + geom_point(),
ggplot(data = mtcars, aes(x = mpg, y = disp, col = am)) + geom_point(),
ggplot(data = mtcars, aes(x = mpg, y = hp, col = am)) + geom_point()
)
Now I can refer to each element and add the title as follows
l[[1]] + ggtitle("Cars")
l[[2]] + ggtitle("Cars")
l[[3]] + ggtitle("Cars")
But is there a way to add the title to all elements in the list at once?
(Note: For one layer, this is rather silly, but I can extend such an example to multiple layers.)
User H 1 answered the question. As ggplot2 is different due to the layering, I was unsure if lapply() would work in this case. I now learned that the pipe symbol, + is a function to be applied over.
But adding a title and positioning the legend at the bottom has the desired effect:
q <- lapply(l, function(x) x + ggtitle("Cars") + theme(legend.position = "bottom"))
multiplot( plotlist = q, cols = 2)
where the code for multiplot() is found here.

Overlapping Trend Lines in scatterplots, R

I am trying to overlay multiple trend lines using the geom_smooth() in R. I currently have this code.
ggplot(mtcars2, aes(x=Displacement, y = Variable, color = Variable))
+ geom_point(aes(x=mpg, y = hp, col = "Power"))
+ geom_point(aes(x=mpg, y = drat, col = "Drag Coef."))
(mtcars2 is the normalized form of mtcars)
Which give me this graph.
I am trying to use the geom_smooth(method='lm') to draw two trend lines for the the two variables. Any ideas?
(Bonus: I would also like to implement the 'shape=1' paramater to differentiate the varaibles if possible. The following method does not work)
geom_point(aes(x=mpg, y = hp, col = "Power", shape=2))
Update
I managed to do this.
ggplot(mtcars2, aes(x=Displacement, y = Variable, color = Variable))
+ geom_point(aes(x=disp, y = hp, col = "Power"))
+ geom_point(aes(x=disp, y = mpg, col = "MPG"))
+ geom_smooth(method= 'lm',aes(x=disp, y = hp, col = "Power"))
+ geom_smooth(method= 'lm',aes(x=disp, y = mpg, col = "MPG"))
It looks like this.
But this is an ugly piece of code. If anybody can make this code look prettier, it'd be great. Also, I have not yet been able to implement the 'shape=2' parameter.
It seems like you're making your life harder than it needs to be...you can pass in additional parameters into aes() such as group and shape.
I don't know if I got your normalization right, but this should give you enough to get going in the right direction:
library(ggplot2)
library(reshape2)
#Do some normalization
mtcars$disp_norm <- with(mtcars, (disp - min(disp)) / (max(disp) - min(disp)))
mtcars$hp_norm <- with(mtcars, (hp - min(hp)) / (max(hp) - min(hp)))
mtcars$drat_norm <- with(mtcars, (drat - min(drat)) / (max(drat) - min(drat)))
#Melt into long form
mtcars.m <- melt(mtcars, id.vars = "disp_norm", measure.vars = c("hp_norm", "drat_norm"))
#plot
ggplot(mtcars.m, aes(disp_norm, value, group = variable, colour = variable, shape = variable)) +
geom_point() +
geom_smooth(method = "lm")
Yielding:

Line size by number of observations of a factor

I have the following plot created from the mtcars data set and this code:
ggplot(mtcars, aes(x = mpg, y = hp, colour = factor(gear))) + geom_point() + geom_smooth(method = lm, se = FALSE)
I want the line size of the linear regression lines to be proportional to the number of observations in each level of factor(gear):
> count(factor(mtcars$gear))
x freq
3 15
4 12
5 5
I've tried calling size = ..count.. and ..n.., inside the main ggplot call and the geom_smooth call with no luck.
Is there a way to do this?
Something like this should work:
library(ggplot2)
library(plyr)
line_size <- count(factor(mtcars$gear))
ggplot(mtcars, aes(x = mpg, y = hp, colour = factor(gear), size=factor(gear))) +
geom_point(size=element_blank()) +
geom_smooth(method = lm, se = FALSE) +
scale_size_manual(values=line_size$freq/4)

ggplot2: Create an empty space on the x-axis

On the below plot, I'd like to create an empty space on the x-axis at position 3. In other word, I'd like that the dots for wt>=3 and the axis scale when wt>=3 are shifted to the right side by some arbitrary chosen value. Does it make sense?
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
I could simply modify the my data and add 0.5 to each value of mpg where wt>=3 but it doesn't solve the issue with the x-axis.
I might rephrase my question saying that on the below graph I'd like that the vertical line does not overlap the data and therefore all the data (and also the x-axis) should be shifted to the left by the thickness of the vertical line.
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + geom_vline(xintercept=3, size=30)
I was thinking about facet_wrap or viewport. Maybe something like adding a constant to each value of mpg where wt>=3 and then, manually set the values for the x-axis.
Not entirely sure what you are looking for, and I get confused by watching the weird axis on my own plot... Something like this?
mtcars$wt2 <- with(mtcars, ifelse(wt > 3, wt+1, wt))
ggplot(mtcars, aes(x = wt2, y = mpg)) +
geom_point() +
annotate("rect", xmin = 3, xmax = 4, ymin = 0, ymax = 35, alpha = 0.2) +
scale_x_continuous(breaks = round(mtcars$wt2), label = round(mtcars$wt))
Similar to # Frank, and tested...
x <- mtcars
x$group <- ""
x[x$wt<3,]$group <- "1.LIGHT"
x[x$wt>=3,]$group <- "2.HEAVY"
library(ggplot2)
library(grid) #for unit(...)
ggplot(x, aes(x=wt,y=mpg)) +
geom_point() +
facet_grid(~group,scales="free",space="free") +
theme(panel.margin = unit(2, "lines"))
Produces this:
what about something like this?
mtcars <- transform(mtcars, split = ifelse(wt >= 3, "b", "a" ))
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
facet_wrap(~split, scales = "free_x")

Resources