Develop a custom Rcpp function to be used with terra::focalCpp to calculate the percent of a specific value within a moving window

huangapple go评论101阅读模式
英文:

Develop a custom Rcpp function to be used with terra::focalCpp to calculate the percent of a specific value within a moving window

问题

以下是您要翻译的内容:

我正在尝试使用terra::focal来加速一些栅格处理,通过使用focalCpp来实现。

这里有一些示例数据,其中包含了1和NAs,以复制实际数据集

```R
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)
 

这是我尝试在Rcpp中复制的原始用于focal的函数:

fxnpercent = function(x) {
  q=x # 复制x
  q[q!=1] = NA # q变为1
  length(q[!is.na(q)])/length(x[!is.na(x)]) * 100 # 获取1的百分比
}

这是原始focal函数,窗口通过200米缓冲区来近似:

# 移动窗口矩阵
mat = matrix(1,15,15) # 创建一个包围缓冲区范围的1矩阵
gr = expand.grid(1:nrow(mat), 1:nrow(mat)) # 基于行/列索引的所有成对值的数据框
center = 8 # 方形网格的中心索引
gr$dist = sqrt((gr$Var1-center)^2 + (gr$Var2-center)^2) # 欧几里德距离计算
threshold = 200/30 # 200米阈值转换为像素数(从中心计算)
gr$inside = ifelse(gr$dist < threshold, 1, NA) # 如果距离小于阈值,则网格值为1,否则为NA
w = matrix(gr$inside, 15,15)  # 使用gr$inside,将索引值放入原始维度的矩阵中

# 输出移动窗口的百分比
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);

		// 循环处理单元格
		size_t start = 0;
		for (size_t i=0; i<ni; i++) {
			size_t end = start + nw;
			// 计算窗口内的值
			double v = 0;
			// 循环处理窗口内的值
			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的语法进行了大量故障排除以确保正确性之后,我尝试使用focalCpp运行此函数,但出现错误。


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

# 我当前的错误

Error: [focalCpp] 测试失败

我认为我需要在Rcpp函数的窗口循环内进行一些计算,但我在如何设置它以正常工作方面遇到了问题。

英文:

I'm trying to speed up some raster processing I'm doing using terra::focal by using focalCpp.

Here is some example data with 1s and NAs included to replicate an actual dataset

nr &lt;- nc &lt;- 50
r &lt;- rast(ncols=nc, nrows=nr, ext= c(0, nc, 0, nr))
values(r) &lt;- 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)
 

This is the original function used with focal that I'm trying to duplicate in 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
}

This is the original focal function with a window approximated by a 200m buffer

# moving window matrix 
mat = matrix(1,15,15) # create matrix of 1&#39;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 &lt; 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=&quot;all&quot;)

And this is my attempt to duplicate the fxnpercent function in Rcpp

cppFunction( 
    &#39;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&lt;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&lt;end; j++) {
				if (x[j] != 1) {
				  v += std::nan(&quot;&quot;);
				}
				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;
	}&#39;
  )

After lots of troubleshooting to get the syntax correct in Rcpp, I try to run this function with focalCpp and I have an error.


percent = focalCpp(r, w=w, fun=fxnpercent, na.policy=&quot;all&quot;)

#my current error

Error: [focalCpp] test failed

I think I need to do some calculations within the window loop in the Rcpp function but I'm having trouble with understanding how to set it up to work correctly.

答案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);
  
  // 循环遍历单元格
  size_t start = 0;
  for (size_t i = 0; i < ni; i++) {
    size_t end = start + nw;
    // 计算窗口内的某些内容
    double v = 0;
    double totalCount = 0;
    double countOfOnes = 0;

    // 循环遍历窗口内的值
    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(""); // 如果全部都是NaN,则为NaN
    }
    
    start = end;
  }
  return out;
}
英文:

NaN checking is not done via is_nan() but rather using std::isnan()

#include&lt;Rcpp.h&gt;
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&lt;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&lt;end; j++) {
      if (!std::isnan(x[j])) {
        totalCount++;
        if (x[j] == 1) {
          countOfOnes++;
        }
      }
    }

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

huangapple
  • 本文由 发表于 2023年7月28日 04:26:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76783194.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定