如何加快R中terra::writeVect的写入速度?处理大型物种矢量数据
问题:处理大型Shapefile时
terra::writeVector速度过慢的优化方案 我有一个约13k行的大型Shapefile(哺乳动物数据),需要保留每个物种的多边形,并根据presence和origin列过滤数据。但使用terra写入输出时速度极慢。
首次尝试代码(出现内存问题)
mam <- terra::vect("./databases/MAMMALS.shp") mam_list <- unique(mam$sci_name) for(sp in mam_list){ # 保留单个物种数据 sp_shp <- mam[mam$sci_name == sp,] # 过滤presence和origin sp_shp <- sp_shp %>% tidyterra::filter(presence < 5) %>% tidyterra::filter(origin < 3) # 保存过滤后的矢量数据 writeVector(sp_shp, paste0("./databases/filtered_shp/", sp, ".gpkg"), overwrite = T) }
后续优化(解决内存问题但速度仍慢)
mam_filt <- mam %>% tidyterra::filter(presence < 5) %>% tidyterra::filter(origin < 3) mam_filt_list <- unique(mam_filt$sci_name) process_species <- function(sp) { sp_shp <- mam_filt[mam_filt$sci_name == sp, ] writeVector(sp_shp, paste0("./databases/filtered_shp/", sp, ".gpkg"), overwrite = TRUE) } lapply(mam_filt_list, process_species)
当前环境:R版本4.4.2,tidyterra版本0.6.1,terra版本1.7-83
疑问:
- 在R中是否有更高效的实现方式(比如使用
sf),或是我忽略了terra的某些优化点? - 在此特定场景下,
writeVector为何如此缓慢?
解决方案与解释
一、更高效的实现方式
1. terra原生优化技巧
- 合并写入到单GPKG的多图层:避免每个物种生成单独文件,利用GPKG支持多图层的特性,一次性写入所有物种,大幅减少文件I/O开销:
# 用原生subset替代tidyterra::filter,减少额外开销 mam_filt <- terra::subset(mam, presence < 5 & origin < 3) # 按sci_name分组写入到同一个GPKG的不同图层 terra::writeVector(mam_filt, "./databases/filtered_shp/all_species.gpkg", layer = mam_filt$sci_name, overwrite = TRUE)
- 预拆分数据再批量写入:如果必须生成单独文件,先通过
terra::split一次性拆分数据,避免循环中重复遍历整个数据集:
# 按物种拆分过滤后的数据 sp_list <- terra::split(mam_filt, "sci_name") # 循环写入,减少函数调用开销 for (sp in names(sp_list)) { terra::writeVector(sp_list[[sp]], paste0("./databases/filtered_shp/", sp, ".gpkg"), overwrite = TRUE) }
- 额外优化:显式指定
filetype="GPKG"避免格式推断,关闭进度条(terra::terraOptions(progress=0))减少交互开销。
2. 使用sf的实现方案
sf底层GDAL调用的I/O优化逻辑更适配小文件批量写入,可尝试:
library(sf) # 读取并过滤数据 mam_sf <- st_read("./databases/MAMMALS.shp") %>% filter(presence < 5, origin < 3) # 按物种拆分后循环写入 sp_list_sf <- split(mam_sf, mam_sf$sci_name) for (sp in names(sp_list_sf)) { st_write(sp_list_sf[[sp]], paste0("./databases/filtered_shp/", sp, ".gpkg"), delete_layer = TRUE, quiet = TRUE) }
quiet=TRUE可关闭写入时的日志输出,进一步减少I/O交互耗时。
二、writeVector速度慢的核心原因
- 频繁文件I/O开销:每个物种生成单独GPKG时,每次写入都要初始化GDAL数据集、写入元数据,频繁的文件创建/关闭是主要耗时点。
- 子集化重复遍历:原代码中每次循环都遍历整个
mam_filt做子集匹配,时间复杂度为O(n*m)(n为物种数,m为数据行数),效率极低。 - tidyterra的额外转换开销:
tidyterra::filter引入了tidyverse管道和数据结构转换的额外开销,相比原生terra::subset更耗时。
内容的提问来源于stack exchange,提问作者LT17
相关产品推荐
相关产品推荐

