使用terra::focal自定义函数遇类型转换错误,求矩阵替代方案
移动窗口自定义函数报错及替代方案
我需要在移动窗口中运行一个自定义函数,之前用terra::focal成功过,但现在遇到错误:
Error in as.vector(x, "character") : cannot coerce type 'closure' to vector of type 'character'
查相关案例提示可能是函数名缺括号,但加括号后又出现新错误。我不一定非要用terra::focal,也接受在矩阵上运行、效率相当的替代方案,且不想来回转换矩阵和SpatRaster(因为不是空间工作流)。
示例代码:
# Custom function: if mean of the window is greater than the threshold # then return the value unchanged, otherwise return the minimum value in the window. # (Returning value number 14, as this is the central cell in a 9 x 3 window) mean.or.min <- function(x, ...){ if(mean(x) > 10) { return(x[[14]]) } else { x[[14]] <- min(x) return(x[[14]]) } } # Create example data set.seed(42) temp.matrix <- matrix(data = sample(1:30, 600000, replace = TRUE), nrow = 200) # Convert matrix to a spatRaster temp.rast <- terra::rast(temp.matrix) # Moving window temp.rast.smoothed <- terra::focal(temp.rast, w = c(9,3), fun = mean.or.min, fillvalue = 0, expand = TRUE)
一、修复terra::focal的用法问题
报错核心原因是terra::focal传递给自定义函数的x是数值向量,而非列表,你用的x[[14]]是列表索引写法,应改为向量索引x[14]。另外expand = TRUE参数在当前terra版本中已不适用,需移除。
修改后的代码:
mean.or.min <- function(x, ...){ if(mean(x) > 10) { return(x[14]) # 向量用[]索引,而非[[ ]] } else { return(min(x)) # 无需修改x元素,直接返回窗口最小值 } } # 调用focal时移除无效的expand参数 temp.rast.smoothed <- terra::focal(temp.rast, w = c(9,3), fun = mean.or.min, fillvalue = 0)
二、直接在矩阵上运行的替代方案(推荐)
使用RcppRoll包的滑动窗口函数,底层基于C++实现,效率和terra::focal相当,且无需转换空间对象:
- 安装并加载包
install.packages("RcppRoll") library(RcppRoll)
- 实现矩阵滑动窗口逻辑
set.seed(42) temp.matrix <- matrix(data = sample(1:30, 600000, replace = TRUE), nrow = 200) # 定义9×3窗口的滑动处理函数 matrix_focal <- function(mat, window_rows = 9, window_cols = 3, threshold = 10) { # 计算窗口内中心元素的位置 center_pos <- (ceiling(window_cols/2)-1)*window_rows + ceiling(window_rows/2) # 滑动窗口处理 result <- RcppRoll::rollapply(mat, width = c(window_rows, window_cols), FUN = function(x) { if(mean(x) > threshold) x[center_pos] else min(x) }, fill = 0, align = "center") return(result) } # 运行得到处理后的矩阵 temp.matrix.smoothed <- matrix_focal(temp.matrix, 9, 3, 10)
说明
align = "center"确保窗口以当前元素为中心,和terra::focal默认行为一致fill = 0对应原代码的边缘填充值设置- 大矩阵场景下,
RcppRoll的执行效率和terra持平,且省去了空间对象转换的步骤
内容的提问来源于stack exchange,提问作者EcologyTom
相关产品推荐
相关产品推荐

