开发一个自定义 Rcpp 函数,与 terra::focalCpp 一起使用来计算移动窗口内特定值的百分比

问题描述 投票:0回答:1

我正在尝试通过使用 focusCpp 来加速我正在使用 terra::focal 进行的一些光栅处理。

这里是一些包含 1 和 NA 的示例数据,用于复制实际数据集

nr <- nc <- 50
r <- rast(ncols=nc, nrows=nr, ext= c(0, nc, 0, nr))
values(r) <- rep(c(rep(NA, 10), rep(1, 10), seq(1,8), rep(1,12), rep(NA,5), rep(1,15),seq(1,8), rep(NA,12), seq(1,20)), 50)
 

这是与 focus 一起使用的原始函数,我试图在 Rcpp 中复制它

fxnpercent = function(x) {
  q=x # make a copy of x
  q[q!=1] = NA # q becomes just 1s
  length(q[!is.na(q)])/length(x[!is.na(x)]) * 100 # gets percentage of 1s
}

这是原始焦点函数,窗口近似为 200m 缓冲区

这是 na.policy="all" 的定义 - 可用于确定应计算焦点值的 x 的单元格。必须是“all”之一(计算所有单元格)。请注意,此参数的值不会影响每个焦点单元周围的哪些单元包含在计算中(使用 na.rm=TRUE 忽略 NA 的单元)

# moving window matrix 
mat = matrix(1,15,15) # create matrix of 1's that envelopes the extent of the buffer
gr = expand.grid(1:nrow(mat), 1:nrow(mat)) # df of all pairwise values based on row/col index
center = 8 # centroid index of the square grid
gr$dist = sqrt((gr$Var1-center)^2 + (gr$Var2-center)^2) # euclidean distance calucation 
threshold = 200/30 # 200m threshold is converted into number of pixels from center
gr$inside = ifelse(gr$dist < threshold, 1, NA) # if distance is less than threshold, grid value is one, otherwise NA
w = matrix(gr$inside, 15,15)  # Using gr$inside, place indexed values into matrix of original dimensions

#output percent from moving window
percent = terra::focal(x=r, w=w, fun=fxnpercent, na.policy="all")

这是我尝试在 Rcpp 中复制 fxnpercent 函数

cppFunction( 
    'NumericVector fxnpercent(NumericVector x, size_t ni, size_t nw) {
        NumericVector out(ni);

        // loop over cells
        size_t start = 0;
        for (size_t i=0; i<ni; i++) {
            size_t end = start + nw;
            // compute something for a window
            double v = 0;
            // loop over the values of a window
            for (size_t j=start; j<end; j++) {
                if (x[j] != 1) {
                  v += std::nan("");
                }
                else {
                  v += x[j];
                }
                
            }
            
            NumericVector x1 = x[!is_na(x)];
            NumericVector v1 = v;
            NumericVector v2 = v1[!is_nan(x)];
            
            size_t v2size = v2.size();
            size_t x1size = x1.size();
            
            out[i] = (v2size / x1size) * 100;
            
            start = end;
        }
        return out;
    }'
  )

经过大量故障排除以确保 Rcpp 中的语法正确,我尝试使用 focusCpp 运行此函数,但出现错误。


percent = focalCpp(r, w=w, fun=fxnpercent, na.policy="all")

#my current error

Error: [focalCpp] test failed

我认为我需要在 Rcpp 函数的窗口循环内进行一些计算,但我无法理解如何将其设置为正确工作。

c++ r raster rcpp terra
1个回答
1
投票

NaN 检查不是通过 is_nan() 完成的,而是使用 std::isnan() 完成

#include<Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector fxnpercent(NumericVector x, size_t ni, size_t nw) {
  NumericVector out(ni);
  
  // loop over cells
  size_t start = 0;
  for (size_t i=0; i<ni; i++) {
    size_t end = start + nw;
    // compute something for a window
    double v = 0;
    double totalCount = 0;
    double countOfOnes = 0;

    // loop over the values of a window
    for (size_t j=start; j<end; j++) {
      if (!std::isnan(x[j])) {
        totalCount++;
        if (x[j] == 1) {
          countOfOnes++;
        }
      }
    }

    if (totalCount > 0) {
      out[i] = (countOfOnes / totalCount) * 100;
    } else {
      out[i] = std::nan(""); // it is NaN if all are NaN
    }
    
    start = end;
  }
  return out;
}

© www.soinside.com 2019 - 2024. All rights reserved.