修正Gif中的图表位置 – 用动态轴标签播放图表

huangapple go评论65阅读模式
英文:

Fix plot position in a Gif - animate plot with dynamic axis labels

问题

我正在尝试制作一个 GIF(每帧中,我想在 y 轴上绘制变量,x 轴上绘制值),但在修复绘图位置方面遇到了一些困难,因为一旦 y 轴上的变量长度发生变化,绘图位置就会移动。

这里是一个可复现的示例:

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) {
  
  # 创建绘图
  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, ""))
                     }) +
    # 设置 y 轴限制
    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="")
  
  # 将绘图保存为 PNG 文件
  ggsave(paste0("frame_", i, ".png"), p, width = 800, height = 400, dpi = 150,units='px')
}

# 生成所有帧
for (i in 1:nrow(data)) {
  generate_frame(data, i)
}

# 创建一个空的图像列表
image_list <- image_blank(width = 800, height = 400)

# 读取每个帧并将其添加到图像列表
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)
}

# 将所有帧合并为 GIF 并保存
image_write(image_list, "animation.gif", format = "gif")

我尝试使用 scale_y_discrete() 中的 expand(),但它不起作用。

如果有人可以向我展示如何修复绘图位置并定义每帧之间的间隔,我将不胜感激。

附注:我也尝试使用 gganimate,但不确定如何动态地使 y 轴上的变量发生变化。

英文:

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 &lt;- c(&quot;data.table&quot;,&quot;tidyverse&quot;,&quot;scales&quot;,&quot;magick&quot;)
lapply(packs,require,character.only=T)
data &lt;- data.table(variable=c(letters[1:6],&quot;agdjfhgbbkf&quot;),
values=c(1:7))
data$variable_levels &lt;- fct_reorder(data$variable, data$values)
generate_frame &lt;- function(data, i) {
# create the plot
p &lt;- ggplot(data[1:i,], aes(x = values, y = variable_levels)) +
geom_col(fill = &quot;#4285F4&quot;, 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, &quot;&quot;))
}) +
# set y-axis limits
theme_bw() +
theme(axis.text = element_text(family = &quot;mono&quot;),
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 = &quot;cm&quot;))+
labs(x=&quot;Value&quot;,y=&quot;&quot;,title=&quot;&quot;)
# save the plot as a png file
ggsave(paste0(&quot;frame_&quot;, i, &quot;.png&quot;), p, width = 800, height = 400, dpi = 150,units=&#39;px&#39;)
}
# generate all the frames
for (i in 1:nrow(data)) {
generate_frame(data, i)
}
# create an empty image list
image_list &lt;- 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 &lt;- paste0(&quot;frame_&quot;, i, &quot;.png&quot;)
frame_image &lt;- image_read(frame_file)
image_list &lt;- c(image_list, frame_image)
}
# combine all frames into a GIF and save it
image_write(image_list, &quot;animation.gif&quot;, format = &quot;gif&quot;)

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 = &quot;identity&quot;, position = &quot;dodge&quot;) +
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, &quot;&quot;))
}
) +
# set y-axis limits
theme_bw() +
theme(axis.text = element_text(family = &quot;mono&quot;),
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=&quot;Value&quot;,y=&quot;&quot;)+
transition_states(variable_levels, wrap = F)  +
shadow_mark()

答案1

得分: 1

这是使用gganimate可能的。如果您的目标是获得带有动画轴标签,我目前看不到绕过自定义注释的方法,例如使用geom_text创建轴标签和使用geom_segment创建刻度。这有点麻烦,因为您需要部分硬编码标签位置并为绘图添加边距。但是,您只需要执行这一步一次

此外,您还需要使用shadow_mark

您可以使用anim_save保存输出。要指定图像分辨率,请参考https://stackoverflow.com/questions/49058567/define-size-for-gif-created-by-gganimate-change-dimension-resolution/61763614#61763614

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)

修正Gif中的图表位置 – 用动态轴标签播放图表

创建于2023-03-07,使用reprex v2.0.2

英文:

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 https://stackoverflow.com/questions/49058567/define-size-for-gif-created-by-gganimate-change-dimension-resolution/61763614#61763614

suppressMessages(library(tidyverse))
library(gganimate)

data &lt;- data.frame(variable = c(letters[1:6], &quot;agdjfhgbbkf&quot;), values = c(1:7))

data$variable_levels &lt;- fct_reorder(data$variable, data$values)

p &lt;-
  ggplot(data, aes(x = values, y = variable_levels)) +
  geom_col(fill = &quot;#4285F4&quot;, 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 = &quot;off&quot;, 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 = &quot;Value&quot;, y = NULL)

p_anim &lt;- p + transition_states(values) + shadow_mark()

animate(p_anim)

修正Gif中的图表位置 – 用动态轴标签播放图表<!-- -->

<sup>Created on 2023-03-07 with reprex v2.0.2</sup>

huangapple
  • 本文由 发表于 2023年3月7日 04:58:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75655793.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定