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

如何基于R语言terra包实现spatRaster的加权核密度估计?

Terra栅格加权核密度估计(KDE)的高效实现优化

问题背景

Terra包内置的density()函数可计算SpatRaster的核密度估计,但不支持加权核密度估计。比如在估算特定土地利用类型的年均温KDE时,该类型在单元格中的占比不同,必须通过权重体现这种差异,此时内置函数无法满足需求。

现有一个自定义函数dwr()实现了加权KDE,但仅通过采样优化大栅格场景的性能,效率仍有提升空间,原函数及示例代码如下:

原自定义函数与基础示例

library(terra)
# Terra内置density函数基础用法
x <- rast(matrix(rnorm(100,10,1), nr = 10, nc = 10))
density(x, plot = F)

# 自定义加权栅格密度函数
dwr <- function(x, w, samp = 1, ...){
  compareGeom(x, w, lyrs = TRUE) # 检查数据与权重栅格的一致性
  w <- subst(w, NA, 0) # 将缺失权重转为0
  if(as.numeric(global(w, min)) < 0) return("Warning: Negative weights") # 检查权重是否为负
  n <- global(x, "notNA")[1,] # 统计有效数据量
  sz <- if(samp <= 1) round(samp * n) else samp # 确定采样数量
  if(sz > n) return("Warning: Sample size is too large") # 检查采样量是否超标
  ss <- spatSample(c(x,w), sz, na.rm = TRUE) # 抽取样本
  ss$wt <- ss[,2]/sum(ss[,2]) # 将权重转为占比
  density(ss[,1], weights = ss$wt, ...) # 计算加权核密度
}

# 函数调用示例
x <- rast(matrix(rnorm(100,10,1), nr = 10, nc = 10))
w <- rast(matrix(rpois(100,5), nr = 10, nc = 10))
dwr(x,w)

高效改进方案

针对大栅格场景,可从以下方向优化性能与精度:

1. 跳过采样,直接批量提取有效数据

放弃采样步骤,直接提取所有有效栅格的数值与权重,减少采样带来的随机误差和额外开销:

dwr_opt1 <- function(x, w, ...){
  # 几何一致性检查
  if(!compareGeom(x, w, lyrs = TRUE, stopOnError = FALSE)){
    stop("x和w栅格的几何属性不匹配")
  }
  # 处理缺失值与负权重
  w <- subst(w, NA, 0)
  if(global(w, min, na.rm = TRUE)[[1]] < 0){
    stop("权重不能为负值")
  }
  # 批量提取有效数据
  vals <- cbind(values(x), values(w))
  vals <- vals[complete.cases(vals[,1]), ] # 仅保留x非NA的行
  if(nrow(vals) == 0){
    stop("无有效数据可计算")
  }
  # 归一化权重
  vals[,2] <- vals[,2]/sum(vals[,2])
  # 计算加权KDE
  density(vals[,1], weights = vals[,2], ...)
}

优势:保留所有有效数据的权重信息,结果更准确;批量提取数据的IO效率远高于采样,适合中大型栅格。

2. 分块处理超大栅格

当栅格数据量超出内存上限时,采用分块读取+加权聚合的方式,避免内存溢出:

dwr_opt2 <- function(x, w, blocksize = 1000, ...){
  # 几何一致性检查
  if(!compareGeom(x, w, lyrs = TRUE, stopOnError = FALSE)){
    stop("x和w栅格的几何属性不匹配")
  }
  w <- subst(w, NA, 0)
  if(global(w, min, na.rm = TRUE)[[1]] < 0){
    stop("权重不能为负值")
  }
  
  # 初始化权重总和与数据容器
  total_w <- 0
  data_list <- list()
  
  # 分块读取栅格
  blocks <- makeBlocks(x, n = blocksize)
  for(i in 1:nrow(blocks)){
    b <- blocks[i,]
    x_block <- readValues(x, row = b$row, nrows = b$nrows, col = b$col, ncols = b$ncols)
    w_block <- readValues(w, row = b$row, nrows = b$nrows, col = b$col, ncols = b$ncols)
    # 过滤x为NA的记录
    valid_idx <- !is.na(x_block)
    x_valid <- x_block[valid_idx]
    w_valid <- w_block[valid_idx]
    if(length(x_valid) == 0) next
    # 累加权重总和,暂存数据
    total_w <- total_w + sum(w_valid)
    data_list[[i]] <- data.frame(x = x_valid, w = w_valid)
  }
  
  if(total_w == 0){
    stop("有效权重总和为0,无法计算")
  }
  
  # 合并数据并归一化权重
  all_data <- do.call(rbind, data_list)
  all_data$w <- all_data$w / total_w
  # 计算加权KDE
  density(all_data$x, weights = all_data$w, ...)
}

优势:通过分块控制内存占用,可处理TB级超大栅格;分块读取的效率远高于全量加载,适合内存有限的环境。

3. 预计算全局权重总和,减少重复计算

原函数在采样后才归一化权重,可提前计算全局权重总和,避免重复求和:

dwr_opt3 <- function(x, w, samp = 1, ...){
  # 几何一致性检查
  if(!compareGeom(x, w, lyrs = TRUE, stopOnError = FALSE)){
    stop("x和w栅格的几何属性不匹配")
  }
  w <- subst(w, NA, 0)
  min_w <- global(w, min, na.rm = TRUE)[[1]]
  if(min_w < 0){
    stop("权重不能为负值")
  }
  total_w <- global(w, sum, na.rm = TRUE)[[1]]
  if(total_w == 0){
    stop("有效权重总和为0,无法计算")
  }
  
  n <- global(x, "notNA")[1,]
  sz <- if(samp <= 1) round(samp * n) else samp
  if(sz > n){
    stop("采样量超过有效数据量")
  }
  
  # 采样时直接使用预计算的全局权重总和归一化
  ss <- spatSample(c(x,w), sz, na.rm = TRUE)
  ss$wt <- ss[,2]/total_w
  density(ss[,1], weights = ss$wt, ...)
}

优势:减少重复计算量,同时保留采样功能,适合不需要全量精度的快速估算场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 06:30:15