I wish to generate a gganimate
object that shows n random points in a specific range. The other limitation is that it should plot 2^n
points, meaning 2, 4, 8, 16, 32, 64...
points. I did this to calculate the decimals of pi
but I wish to plot this animation so I can show how it improves the results given more random numbers in a nicer way.
This is what I have so far:
results <- c()
for(i in c(1:20)) {
r <- 1
limit <- 2^i
points <- data.frame(
x = runif(limit, -r, r),
y = runif(limit, -r, r))
points$d <- sqrt(points$x^2 + points$y^2)
points$type <- ifelse(points$d < r, "c", "s")
picalc <- 4 * length(points$type[points$type=="c"]) / limit
error <- pi - picalc
label <- paste0('Pi calc : ', round(picalc, 6), '\nError : ', round(error, 6))
iter <- data.frame(n = limit, picalc = picalc, error = error, label = label)
results <- rbind(results, iter)
}
# GGANIMATE
library(ggplot2)
library(gganimate)
p <- ggplot(results, aes(x = runif(n, -1, 1), y = runif(n, -1, 1))) +
geom_point(lwd = 2, alpha = 0.3) +
theme_minimal() +
geom_text(aes(x = 0, y = 0, label = label), size = 5) +
labs(caption = 'Number of random points : {frame_time}') +
transition_time(n)
animate(p, nframes = nrow(results), fps = 5)
Any suggestions?
Here's how I would "show how it improves the results given more random numbers in a nicer way."
library(ggplot2)
library(gganimate)
p <- ggplot(results, aes(x = n, y = error)) +
geom_point(lwd = 2, alpha = 0.3) +
theme_minimal() +
geom_text(aes(x = 0, y = 0, label = label), size = 5, hjust = 0) +
scale_x_log10(breaks = c(2^(1:4), 4^(2:10)), minor_breaks = NULL) +
labs(caption = 'Number of random points : {2^frame}') +
transition_manual(n) +
shadow_trail(exclude_layer = 2)
animate(p, nframes = nrow(results), fps = 5)
To show the kind of picture described in the question, you'd need to have the points labeled with the frame they belong to. (Also, as currently written, the points are randomly assigned afresh each iteration. It would be better to set all your points first, and then stick with those, in growing window sizes, to calculate the results.)
To do this quickly, I'm taking the last points
frame (as it exists when i
is at the end of the loop), and adding a number for what frame it should belong to. Then I can plot the points of each frame using transition_manual
, and keep the past frames using shadow_trail
.
Note, ggplot will be slower than I cared to wait if you run it with 1M points, so I did an abridged version up to 2^15 = 32k.
# Note, I only ran the orig loop for 2^(1:15), lest it get too slow
points2 <- points %>%
mutate(row = row_number(),
count = 2^ceiling(log2(row)))
point_plot <- ggplot(points2,
aes(x = x, y = y, color = type, group = count)) +
geom_point(alpha = 0.6, size = 0.1) +
theme_minimal() +
labs(caption = 'Number of random points : {2^(frame-1)}') +
transition_manual(count) +
shadow_trail()
animate(point_plot, nframes = 15, fps = 2)