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

使用gganimate与ggplot2遇错误:世界地图动画实现问题

问题描述

我想用rnaturalearth制作随年份变化的世界地图数值动画,已完成数据下载与合并:

baseBorderMap <- 
  ne_countries(returnclass = 'sf') # 获取世界国家多边形的sf格式数据
baseBorderMap["51", "iso_a2_eh"] <- "NM" # 修改纳米比亚的国家代码
baseBorderMap["175", "iso_a2_eh"] <- "XK" # 修改科索沃的国家代码
aniData <- baseBorderMap %>% right_join(countryYearMam, by = "iso_a2_eh") # 将地图数据与含国家代码、数值、年份的数据集合并
aniData <- filter(aniData, !is.na(specimens)) # 移除无标本数据的记录

忽略年份时,用ggplot2可正常绘制静态地图:

mammalYear <- ggplot(data = aniData) + # 绘制地图
  geom_sf(data = aniData, lwd = 0) +
  geom_sf(data = aniData, aes(fill = log(specimens)), lwd = 0.3) + # 按标本数量的对数设置填充色
  scale_fill_viridis_c() +
  theme_bw() +
  theme(legend.title = element_blank())

但添加gganimate函数后,调用animate()或预览时出现错误:

timelapse_mammalYear <- mammalYear +
  transition_states(year,
                    transition_length = 2,
                    state_length = 1) +
  enter_fade() +
  exit_fade()

> timelapse_mammalYear
Error in vapply(from, nrow, integer(1)) : values must be length 1,
 but FUN(X[[8]]) result is length 0
In addition: Warning message:
In max(area_diff) : no non-missing arguments to max; returning -Inf

补充信息:

  • 所有列无NA值
  • 数据集样例:
> dput(head(countryYearMam, 5))
structure(list(iso_a2_eh = c("AD", "AD", "AD", "AD", "AD"), year = c(1930L, 
1950L, 1962L, 1965L, 1968L), specimens = c(1L, 12L, 24L, 14L, 
3L)), row.names = c(NA, 5L), class = "data.frame")
  • 年份范围:1793-2020;specimens范围:1-2601

请问该错误含义是什么?如何修改数据以适配gganimate实现地图动画?


错误原因与解决方案

错误含义

这个错误是因为gganimate处理sf地理数据的状态切换时,部分年份对应的地理多边形数据为空(长度为0)。具体来说,transition_states会按year分组处理数据,但有些年份下没有匹配到任何国家的地理多边形,导致vapply无法得到长度为1的结果;同时计算地理面积差异时没有有效值,触发警告。

解决方案

1. 补全年份-国家的完整组合

你的countryYearMam数据只有部分年份的部分国家数据,right_join后又过滤掉了无标本数据的记录,导致部分年份的地理数据不完整。需要为每个年份补全所有国家的记录,无标本数据的国家设为0(避免后续log计算报错):

# 生成所有年份与国家代码的完整组合
full_year_country <- expand.grid(
  iso_a2_eh = unique(baseBorderMap$iso_a2_eh),
  year = unique(countryYearMam$year),
  stringsAsFactors = FALSE
)

# 合并原始数据,补全缺失的标本数据为0
full_data <- full_year_country %>%
  left_join(countryYearMam, by = c("iso_a2_eh", "year")) %>%
  mutate(specimens = ifelse(is.na(specimens), 0, specimens))

# 再与地图数据合并
aniData_full <- baseBorderMap %>%
  right_join(full_data, by = "iso_a2_eh")

2. 简化静态地图代码

无需重复调用geom_sf,同时处理log(0)的问题:

mammalYear <- ggplot(data = aniData_full) +
  geom_sf(aes(fill = log(specimens + 1)), lwd = 0.3) + # +1避免log(0)报错
  scale_fill_viridis_c() +
  theme_bw() +
  theme(legend.title = element_blank())

3. 调整动画逻辑并生成动画

如果某些年份所有国家标本数都为0(比如早期年份),可以过滤掉这些年份,同时添加年份标题提升可读性:

# 过滤掉所有国家标本数都为0的年份
aniData_filtered <- aniData_full %>%
  group_by(year) %>%
  filter(sum(specimens) > 0) %>%
  ungroup()

# 制作动画
timelapse_mammalYear <- ggplot(data = aniData_filtered) +
  geom_sf(aes(fill = log(specimens + 1)), lwd = 0.3) +
  scale_fill_viridis_c() +
  theme_bw() +
  theme(legend.title = element_blank()) +
  transition_states(year,
                    transition_length = 2,
                    state_length = 1) +
  enter_fade() +
  exit_fade() +
  labs(title = "年份: {closest_state}") # 添加动态年份标题

# 生成动画(可调整宽高适配需求)
animate(timelapse_mammalYear, width = 1200, height = 800)

额外提示

  • 检查baseBorderMap中的iso_a2_eh是否与countryYearMam中的代码完全匹配,避免因代码不匹配导致的国家数据缺失
  • 如果动画运行缓慢,可以用ne_countries(scale = "medium")或"small"简化地理数据,降低多边形复杂度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 18:43:27