You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在gganimate中保留散点同时动态展示回归拟合线?

解决gganimate中保留散点并动态展示回归拟合线的问题

问题背景

你有如下数据框:

df <- data.frame(
  Year = rep(c(2003, 2006, 2009, 2012, 2015, 2018, 2022), 3),
  Average = c(539, 538, 543, 554, 542, 544, 546,
              498, 489, 488, 488, 487, 487, 472,
              508, 501, 507, 500, 493, 495, 481),
  code = rep(c("SE ASIA", "EUR27", "North America"), each = 7)
)

并制作了静态ggplot图:

p <- ggplot(df, aes(x = Year, y = Average, color = code)) +
  geom_point(size = 3) +
  stat_smooth(method = "lm", se = TRUE, linewidth = 0.85, aes(fill = code),alpha = 0.25, show.legend = FALSE) +
  labs(title = "Evolução do Average por Região",
       x = "Ano", y = "Average") +
  theme_minimal() +
  theme(legend.title = element_blank()) 

尝试用transition_time制作动画时,因第一帧仅单个点无法拟合回归线报错:

`geom_smooth()` using formula = 'y ~ x'
Error in `$<-.data.frame`(`*tmp*`, "group", value = "") : 
  replacement has 1 row, data has 0

之后你按code分组预计算了拟合值:

df <- df %>%
  group_by(code) %>%
  mutate(smooth_values = predict(lm(Average ~ Year, data = cur_data())))

但用transition_reveal展示拟合线时,无法保留已出现的散点。


解决方案

核心是分开处理散点和拟合线的动画逻辑,确保散点随年份依次出现并保留,拟合线逐步展示(或基于累计数据动态更新)。

方法1:基于累计数据动态计算拟合线(贴合“逐步拟合”逻辑)

这个方法会让拟合线随着每年新增的数据实时更新,同时保留所有已出现的散点:

library(tidyverse)
library(gganimate)

# 生成每个年份的累计数据集:按code分组,保留当前年份及之前的所有数据
df_anim <- df %>%
  group_by(code) %>%
  arrange(Year) %>%
  group_split() %>%
  map_dfr(function(region_data) {
    region_data %>%
      mutate(reveal_year = Year) %>%
      group_by(reveal_year) %>%
      # 补全当前reveal_year之前的所有年份数据
      complete(Year = first(Year):reveal_year) %>%
      fill(Average, .direction = "down") %>%
      ungroup()
  }) %>%
  # 对每个code的每个累计年份,重新计算拟合值
  group_by(code, reveal_year) %>%
  mutate(smooth_values = predict(lm(Average ~ Year, data = cur_data()))) %>%
  ungroup()

# 制作动画
anim <- ggplot(df_anim, aes(x = Year, color = code)) +
  # 绘制所有已出现的散点
  geom_point(aes(y = Average), size = 3) +
  # 绘制基于累计数据的拟合线
  geom_line(aes(y = smooth_values), linewidth = 0.85) +
  labs(title = "Evolução do Average por Região",
       x = "Ano", y = "Average") +
  theme_minimal() +
  theme(legend.title = element_blank()) +
  # 按reveal_year逐步展示,保留所有历史元素
  transition_reveal(reveal_year) +
  # 固定Y轴范围,避免动画过程中坐标轴跳动
  view_follow(fixed_y = TRUE)

# 渲染动画
animate(anim)

方法2:用预计算的完整拟合线,逐步展示并保留散点

如果不需要拟合线随数据更新,只是想逐步展示预计算好的完整拟合线,同时保留散点:

library(tidyverse)
library(gganimate)

# 预计算拟合值(你已完成的步骤)
df <- df %>%
  group_by(code) %>%
  mutate(smooth_values = predict(lm(Average ~ Year, data = cur_data()))) %>%
  ungroup()

# 制作动画
anim <- ggplot(df, aes(x = Year, color = code)) +
  # 散点:用shadow_mark确保已出现的点不消失
  geom_point(aes(y = Average), size = 3) +
  # 拟合线:按code分组,确保每条线独立展示
  geom_line(aes(y = smooth_values, group = code), linewidth = 0.85) +
  labs(title = "Evolução do Average por Região",
       x = "Ano", y = "Average") +
  theme_minimal() +
  theme(legend.title = element_blank()) +
  # 按Year逐步展示元素
  transition_reveal(Year) +
  # 强制保留所有已绘制的散点
  shadow_mark() +
  view_follow(fixed_y = TRUE)

# 渲染动画
animate(anim)

内容的提问来源于stack exchange,提问作者JPMD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:59:50