I am trying to make a gif(for each frame I want to plot the variable in the y axis, and the value in x axis), but bit struggled to fix the plot position since it will move once the length of the variable in y axis changes.
Here is one reproducible example:
packs <- c("data.table","tidyverse","scales","magick")
lapply(packs,require,character.only=T)
data <- data.table(variable=c(letters[1:6],"agdjfhgbbkf"),
values=c(1:7))
data$variable_levels <- fct_reorder(data$variable, data$values)
generate_frame <- function(data, i) {
# create the plot
p <- ggplot(data[1:i,], aes(x = values, y = variable_levels)) +
geom_col(fill = "#4285F4", position = position_dodge()) +
scale_x_continuous(limits = c(0, max(data$values)),labels = scales::comma) +
scale_y_discrete(limits = rev(levels(data$variable_levels)),
labels = function(x) {
wrap_format(10)(ifelse(x %in% data$variable_levels[1:i], x, ""))
}) +
# set y-axis limits
theme_bw() +
theme(axis.text = element_text(family = "mono"),
panel.grid.major.y = element_blank(),
axis.line.y = element_blank(),
axis.text.y = element_text(hjust = 1, size = 10),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
plot.margin = margin(t = 1, r = 1, b = 1, l = 5, unit = "cm"))+
labs(x="Value",y="",title="")
# save the plot as a png file
ggsave(paste0("frame_", i, ".png"), p, width = 800, height = 400, dpi = 150,units='px')
}
# generate all the frames
for (i in 1:nrow(data)) {
generate_frame(data, i)
}
# create an empty image list
image_list <- image_blank(width = 800, height = 400)
# read in each frame and add it to the image list
for (i in 1:nrow(data)) {
frame_file <- paste0("frame_", i, ".png")
frame_image <- image_read(frame_file)
image_list <- c(image_list, frame_image)
}
# combine all frames into a GIF and save it
image_write(image_list, "animation.gif", format = "gif")
I have tried to use expand()
in scale_y_discrete()
, but it does not work.
I would appreciate it if someone can show me how to fix the plot position and define the interval between each frame.
PS: also tried to use gganimate
, but not sure how to make the occurence of the variable in y axis dynamically
library(gganimate)
library(ggplot2)
ggplot(data, aes(x = variable, y = values)) +
geom_bar(stat = "identity", position = "dodge") +
scale_x_continuous(limits = c(0, max(data$values)),labels = scales::comma) +
scale_y_discrete(limits = rev(levels(data$variable_levels)),
labels = function(x) {
wrap_format(20)(ifelse(x %in% data$variable_levels, x, ""))
}
) +
# set y-axis limits
theme_bw() +
theme(axis.text = element_text(family = "mono"),
panel.grid.major.y = element_blank(),
axis.line.y = element_blank(),
axis.text.y = element_text(hjust = 1, size = 10),
axis.ticks.y = element_blank(),
axis.title.y = element_blank())+
labs(x="Value",y="")+
transition_states(variable_levels, wrap = F) +
shadow_mark()
This is possible with gganimate. If your aim is to get animated axis labels, I don't currently see a way around custom annotation - e.g., axis labels with geom_text and ticks with geom_segment. This is a bit painful, as you need to partially hard code your label positions and also add a margin to your plot. However, you only need to do this once.
Additionally, you will need shadow_mark
.
You can save the output with anim_save
. To specify your image resolution, follow Define size for .gif created by gganimate - change dimension / resolution
suppressMessages(library(tidyverse))
library(gganimate)
data <- data.frame(variable = c(letters[1:6], "agdjfhgbbkf"), values = c(1:7))
data$variable_levels <- fct_reorder(data$variable, data$values)
p <-
ggplot(data, aes(x = values, y = variable_levels)) +
geom_col(fill = "#4285F4", position = position_dodge()) +
## size and x nudge are hard coded - might need adjustment.
geom_text(aes(x = 0, label = variable_levels), size = 10 * 5 / 14, hjust = 1, nudge_x = -.1) +
## now the ticks with geom_segment
geom_segment(aes(x = -.05, xend = 0, yend = variable_levels)) +
scale_x_continuous(expand = c(0, 0), labels = scales::comma) +
## need to turn off clipping
coord_cartesian(clip = "off", xlim = c(0, NA)) +
theme( ## remove y axis labels and ticks
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
## add a margin to y title and to the plot
plot.margin = margin(l = 60, r = 2)
) +
## use NULL to remove titles
labs(x = "Value", y = NULL)
p_anim <- p + transition_states(values) + shadow_mark()
animate(p_anim)
Created on 2023-03-07 with reprex v2.0.2