使用 Rcpp 重写 R 的 cummin() 函数并允许 NA
Rewriting R's cummin() function using Rcpp and allowing for NAs
我正在学习 Rcpp。在这个例子中,我试图像基础 R 的 cummin()
一样滚动我自己的 cummin()
函数,除了我希望我的版本有一个 na.rm
参数。这是我的尝试
cummin.cpp
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector cummin_cpp(NumericVector x, bool narm = false){
// Given a numeric vector x, returns a vector of the
// same length representing the cumulative minimum value
// if narm = true, NAs will be ignored (The result may
// contain NAs if the first values of x are NA.)
// if narm = false, the resulting vector will return the
// cumulative min until the 1st NA value is encountered
// at which point all subsequent entries will be NA
if(narm){
// Ignore NAs
for(int i = 1; i < x.size(); i++){
if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1];
}
} else{
// Don't ignore NAs
for(int i = 1; i < x.size(); i++){
if(NumericVector::is_na(x[i-1]) | NumericVector::is_na(x[i])){
x[i] = NA_REAL;
} else if(x[i-1] < x[i]){
x[i] = x[i-1];
}
}
}
return x;
}
foo.R
library(Rcpp)
sourceCpp("cummin.cpp")
x <- c(3L, 1L, 2L)
cummin(x) # 3 1 1
cummin_cpp(x) # 3 1 1
class(cummin(x)) # integer
class(cummin_cpp(x)) # numeric
我有几个问题..
- R 的标准变量名是
na.rm
,而不是我做的narm
。但是,我似乎不能在 C++ 变量名中使用点。有没有办法解决这个问题,这样我就可以与 R 的约定保持一致?
- 我事先不知道用户的输入是数字向量还是整数向量,所以我使用了 Rcpp 的 NumericVector 类型。不幸的是,如果输入是整数,则输出将转换为数字,这与基本 R 的
cummin()
行为不同。人们通常如何处理这个问题?
if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1];
行似乎很愚蠢,但我不知道有什么更好的方法。这里有建议吗?
我会用这个:
template<typename T, int RTYPE>
Vector<RTYPE> cummin_cpp2(Vector<RTYPE> x, bool narm){
Vector<RTYPE> res = clone(x);
int i = 1, n = res.size();
T na;
if(narm){
// Ignore NAs
for(; i < n; i++){
if(ISNAN(res[i]) || (res[i-1] < res[i])) res[i] = res[i-1];
}
} else{
// Do not ignore NAs
for(; i < n; i++){
if(ISNAN(res[i-1])) {
na = res[i-1];
break;
} else if(res[i-1] < res[i]){
res[i] = res[i-1];
}
}
for(; i < n; i++){
res[i] = na;
}
}
return res;
}
// [[Rcpp::export]]
SEXP cummin_cpp2(SEXP x, bool narm = false) {
switch (TYPEOF(x)) {
case INTSXP: return cummin_cpp2<int, INTSXP>(x, narm);
case REALSXP: return cummin_cpp2<double, REALSXP>(x, narm);
default: Rcpp::stop("SEXP Type Not Supported.");
}
}
试穿:
x <- c(NA, 7, 5, 4, NA, 2, 4)
x2 <- as.integer(x)
cummin_cpp(x, narm = TRUE)
x
cummin_cpp(x2)
x2
x <- c(NA, 7, 5, 4, NA, 2, 4)
x2 <- as.integer(x)
x3 <- replace(x, is.na(x), NaN)
cummin_cpp2(x, narm = TRUE)
x
cummin_cpp2(x2)
x2
cummin_cpp2(x3)
x3
解释:
- Joran 的建议很好,只需将其包装在 R 函数中
- 我按照 Joseph Wood 的建议使用调度程序
- 注意
x
是通过引用传递的,如果与您声明的类型相同(请参阅 these 2 slides)
- 您需要处理
NA
以及 NaN
- 您可以使用
||
而不是 |
来仅评估第一个条件是否为真。
我正在学习 Rcpp。在这个例子中,我试图像基础 R 的 cummin()
一样滚动我自己的 cummin()
函数,除了我希望我的版本有一个 na.rm
参数。这是我的尝试
cummin.cpp
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector cummin_cpp(NumericVector x, bool narm = false){
// Given a numeric vector x, returns a vector of the
// same length representing the cumulative minimum value
// if narm = true, NAs will be ignored (The result may
// contain NAs if the first values of x are NA.)
// if narm = false, the resulting vector will return the
// cumulative min until the 1st NA value is encountered
// at which point all subsequent entries will be NA
if(narm){
// Ignore NAs
for(int i = 1; i < x.size(); i++){
if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1];
}
} else{
// Don't ignore NAs
for(int i = 1; i < x.size(); i++){
if(NumericVector::is_na(x[i-1]) | NumericVector::is_na(x[i])){
x[i] = NA_REAL;
} else if(x[i-1] < x[i]){
x[i] = x[i-1];
}
}
}
return x;
}
foo.R
library(Rcpp)
sourceCpp("cummin.cpp")
x <- c(3L, 1L, 2L)
cummin(x) # 3 1 1
cummin_cpp(x) # 3 1 1
class(cummin(x)) # integer
class(cummin_cpp(x)) # numeric
我有几个问题..
- R 的标准变量名是
na.rm
,而不是我做的narm
。但是,我似乎不能在 C++ 变量名中使用点。有没有办法解决这个问题,这样我就可以与 R 的约定保持一致? - 我事先不知道用户的输入是数字向量还是整数向量,所以我使用了 Rcpp 的 NumericVector 类型。不幸的是,如果输入是整数,则输出将转换为数字,这与基本 R 的
cummin()
行为不同。人们通常如何处理这个问题? if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1];
行似乎很愚蠢,但我不知道有什么更好的方法。这里有建议吗?
我会用这个:
template<typename T, int RTYPE>
Vector<RTYPE> cummin_cpp2(Vector<RTYPE> x, bool narm){
Vector<RTYPE> res = clone(x);
int i = 1, n = res.size();
T na;
if(narm){
// Ignore NAs
for(; i < n; i++){
if(ISNAN(res[i]) || (res[i-1] < res[i])) res[i] = res[i-1];
}
} else{
// Do not ignore NAs
for(; i < n; i++){
if(ISNAN(res[i-1])) {
na = res[i-1];
break;
} else if(res[i-1] < res[i]){
res[i] = res[i-1];
}
}
for(; i < n; i++){
res[i] = na;
}
}
return res;
}
// [[Rcpp::export]]
SEXP cummin_cpp2(SEXP x, bool narm = false) {
switch (TYPEOF(x)) {
case INTSXP: return cummin_cpp2<int, INTSXP>(x, narm);
case REALSXP: return cummin_cpp2<double, REALSXP>(x, narm);
default: Rcpp::stop("SEXP Type Not Supported.");
}
}
试穿:
x <- c(NA, 7, 5, 4, NA, 2, 4)
x2 <- as.integer(x)
cummin_cpp(x, narm = TRUE)
x
cummin_cpp(x2)
x2
x <- c(NA, 7, 5, 4, NA, 2, 4)
x2 <- as.integer(x)
x3 <- replace(x, is.na(x), NaN)
cummin_cpp2(x, narm = TRUE)
x
cummin_cpp2(x2)
x2
cummin_cpp2(x3)
x3
解释:
- Joran 的建议很好,只需将其包装在 R 函数中
- 我按照 Joseph Wood 的建议使用调度程序
- 注意
x
是通过引用传递的,如果与您声明的类型相同(请参阅 these 2 slides) - 您需要处理
NA
以及NaN
- 您可以使用
||
而不是|
来仅评估第一个条件是否为真。