使用list_rbind()合并数据时遇行数不匹配错误求助
并行化代码map部分报错排查
问题背景
并行化后的代码在map环节出现行数不匹配错误,但单独运行map内部的串行逻辑完全正常,输出符合预期。
并行报错代码
library(terra) library(stringr) library(dplyr) library(purrr) library(furrr) g <- terra::vect("prueba.SHP") x <- list.files(pattern="\\.tif$") irr <- function(x) { ra <- rast(x) segments <- cut(seq_len(nrow(g)), parallel::detectCores()) slevels <- levels(segments) l <- future_map(slevels, function(level) { rows_to_process <- which(segments == level) map(rows_to_process, function(i) { d <- terra::extract(ra, g[i], exact = TRUE) d <- na.omit(d) if (nrow(d)!=0){ d <- d %>% group_by(ID) %>% summarize(ss = sum(fraction * d[,2])) d <- ((d$ss * 250 * 250) * 100) / g[i]$area_m2 d <- data.frame("gage"=as.numeric(g[i]$GAGE_ID),"perc_irr"=d) } else { d <- data.frame("gage"=as.numeric(g[i]$GAGE_ID),"perc_irr"=0) } return(d) }) %>% list_rbind() }) val <- list_rbind(l) write.csv(val,paste(str_sub(x,end=-5),".csv",sep="")) } for(tif_file in list.files(pattern="\\.tif$")) { irr(tif_file) }
报错信息
Error in (function (... row.names = NULL check.rows = FALSE check.names = TRUE : arguments imply differing number of rows: 1 0
串行正常代码
g <- terra::vect("prueba.SHP") x <- list.files(pattern="\\.tif$") for (i in 1:nrow(g)) { ra <- rast(x[1]) d <- terra::extract(ra, g[i], exact = TRUE) d <- na.omit(d) if (nrow(d)!=0){ d <- d %>% group_by(ID) %>% summarize(ss = sum(fraction * d[,2])) d <- ((d$ss * 250 * 250) * 100) / g[i]$area_m2 d <- data.frame("gage"=as.numeric(g[i]$GAGE_ID),"perc_irr"=d) } else { d <- data.frame("gage"=as.numeric(g[i]$GAGE_ID),"perc_irr"=0) } print(d) }
串行输出示例
gage perc_irr 1 1013500 0.0208757 gage perc_irr 1 1015800 0 gage perc_irr 1 1011000 0
原因分析
- 并行环境对象传递问题:
future_map的子进程无法正确序列化访问外部对象g,导致部分循环中g[i]$area_m2或g[i]$GAGE_ID返回空值,生成行数异常的空数据框,合并时触发行数不匹配错误。 - 手动分段的空分组问题:用
cut按核心数分段时,如果nrow(g)小于核心数,会出现空分组,对应的rows_to_process为空,map返回空列表,list_rbind生成0行数据框,后续合并时与正常1行数据框冲突。 - 缺少并行环境初始化:原代码未调用
plan()初始化并行策略,furrr默认可能以串行模式运行,但分段逻辑依然存在空分组问题。
解决办法
修改后的并行代码
library(terra) library(stringr) library(dplyr) library(purrr) library(furrr) # 初始化多会话并行环境 plan(multisession) g <- terra::vect("prueba.SHP") x <- list.files(pattern="\\.tif$") irr <- function(x) { ra <- rast(x) # 直接并行遍历所有要素行,furrr自动处理负载均衡 l <- future_map(seq_len(nrow(g)), function(i) { d <- terra::extract(ra, g[i], exact = TRUE) d <- na.omit(d) if (nrow(d) != 0){ d <- d %>% group_by(ID) %>% summarize(ss = sum(fraction * !!sym(names(d)[2]))) # 使用sym避免直接引用d[,2]的列名问题 d <- ((d$ss * 250 * 250) * 100) / g[i]$area_m2 d <- data.frame(gage = as.numeric(g[i]$GAGE_ID), perc_irr = d) } else { d <- data.frame(gage = as.numeric(g[i]$GAGE_ID), perc_irr = 0) } return(d) }, .options = furrr_options(globals = list(g = g, ra = ra))) # 显式传递全局对象到子进程 val <- list_rbind(l) write.csv(val, paste0(str_sub(x, end=-5), ".csv"), row.names = FALSE) } for(tif_file in list.files(pattern="\\.tif$")) { irr(tif_file) }
关键修改点
- 调用
plan(multisession)初始化并行环境,确保furrr能正常启动子进程。 - 移除手动分段逻辑,直接用
future_map遍历所有要素行,由furrr自动分配任务到各个核心,避免空分组问题。 - 使用
.options = furrr_options(globals = list(g = g, ra = ra))显式传递g和ra到子进程,解决对象序列化问题。 - 将
d[,2]替换为!!sym(names(d)[2]),避免直接引用列索引可能导致的列名识别问题,提升代码鲁棒性。 - 用
paste0替代paste(..., sep=""),简化字符串拼接。
额外注意事项
- 如果要素数量较少,并行可能比串行效率更低,此时可以切换为串行模式:
plan(sequential)。 - 运行完成后可以用
plan(sequential)关闭并行环境,释放资源。
内容的提问来源于stack exchange,提问作者Sss
相关产品推荐
相关产品推荐

