使用 "NA" 将 R 数组传递给 C 函数
passing in R array to C function with "NA"
我使用 C 库在 R 中工作。我需要向 C 函数传递一个数字在 1 到 10 之间的数组,但也可以是 "NA"。然后在 C 中,根据我需要设置输出的值。
这是一个简化的代码
heredyn.load("ranking.so")
fun <- function(ranking) {
nrak <- length(ranking)
out <- .C("ranking", as.integer(nrak), as.character(ranking), rr = as.integer(vector("integer",nrak)))
out$rr
}
ranking <- sample(c(NA,seq(1,10)),10,replace=TRUE)
rr <- fun(ranking)
C 函数可以简单地是这样
#include <R.h>
void ranking(int *nrak, char *ranking, int *rr) {
int i ;
for (i=0;i<*nrak;i++) {
if (ranking[i] == 'NA')
rr[i] = 1 ;
else
rr[i] = (int) strtol(&ranking[i],(char **)NULL,10) ;
}
}
由于 "NA" 值,我将排名设置为字符,但也许还有另一种方法可以做到这一点,使用整数并且在调用函数之前不将 "NA" 替换为 0?
(像这样的代码,总是给我一个零数组...)
您正试图解决几个微妙且重要的问题,尤其是如何使用 R 编译代码以及测试非有限值。
您寻求有关 C 的帮助。我建议您使用 C++——您不需要以复杂的方式使用它。考虑这个包含一个函数的短文件,该函数可以按照您建议的方式处理向量(我只是测试 NA
然后将 42 指定为 simplicit 的标记)或者对值进行平方:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector foo(NumericVector x) {
unsigned int n = x.size();
for (unsigned int i=0; i<n; i++)
if (NumericVector::is_na(x[i]))
x[i] = 42.0;
else
x[i] = pow(x[i], 2);
return x;
}
/*** R
foo( c(1, 3, NA, NaN, 6) )
*/
如果我将其保存在我的盒子上作为 /tmp/foo.cpp
,以便编译、link、加载甚至 运行 嵌入式 R 使用示例,我只需要一行来调用sourceCpp()
:
R> Rcpp::sourceCpp("/tmp/foo.cpp")
R> foo( c(1, 3, NA, NaN, 6))
[1] 1 9 42 42 36
R>
我们可以对整数做同样的事情:
// [[Rcpp::export]]
IntegerVector bar(IntegerVector x) {
unsigned int n = x.size();
for (unsigned int i=0; i<n; i++)
if (IntegerVector::is_na(x[i]))
x[i] = 42;
else
x[i] = pow(x[i], 2);
return x;
}
使用 R_NaInt
测试该值是否为 NA,如
#include <R.h>
void ranking_c(int *nrak, int *ranking, int *rr) {
for (int i=0; i < *nrak; i++)
rr[i] = R_NaInt == ranking[i] ? -1 : ranking[i];
}
通过明确允许 NA 从 R 调用
> x = c(1:2, NA_integer_)
> .C("ranking_c", length(x), as.integer(x), integer(length(x)), NAOK=TRUE)[[3]]
[1] 1 2 -1
或者,使用 R 的 .Call()
接口。每个 R 对象都表示为一个 S 表达式。有一些 C 级函数可以操作 S 表达式,例如长度 Rf_length()
、数据访问 INTEGER()
和分配 Rf_allocVector()
不同类型的 S 表达式,例如用于整数向量的 INTSXP。
R 内存管理使用垃圾收集器,可以 运行 在任何分配内存的调用上。因此,最佳做法是 PROTECT()
在范围内的任何 R 分配。
您的函数将接受 0 个或多个 S 表达式作为输入,并且 return 单个 S 表达式;它可能被实现为
#include <Rinternals.h>
#include <R_ext/Arith.h>
SEXP ranking_call(SEXP ranking)
{
/* allocate space for result, PROTECTing from garbage collection */
SEXP result = PROTECT(Rf_allocVector(INTSXP, Rf_length(ranking)));
/* assign result */
for (int i = 0; i < Rf_length(ranking); ++i)
INTEGER(result)[i] =
R_NaInt == INTEGER(ranking)[i] ? -1 : INTEGER(ranking)[i];
UNPROTECT(1); /* no more need to protect */
return result;
}
并使用 .Call("ranking_call", as.integer(ranking))
从 R 中调用。
在速度和内存分配方面,使用 .Call
比 .C
更有效(.C 可能会在途中复制原子向量),但使用它的主要原因是它在直接使用 R 的数据结构方面提供了灵活性。当 return 值比原子向量更复杂时,这一点尤为重要。
我使用 C 库在 R 中工作。我需要向 C 函数传递一个数字在 1 到 10 之间的数组,但也可以是 "NA"。然后在 C 中,根据我需要设置输出的值。 这是一个简化的代码
heredyn.load("ranking.so")
fun <- function(ranking) {
nrak <- length(ranking)
out <- .C("ranking", as.integer(nrak), as.character(ranking), rr = as.integer(vector("integer",nrak)))
out$rr
}
ranking <- sample(c(NA,seq(1,10)),10,replace=TRUE)
rr <- fun(ranking)
C 函数可以简单地是这样
#include <R.h>
void ranking(int *nrak, char *ranking, int *rr) {
int i ;
for (i=0;i<*nrak;i++) {
if (ranking[i] == 'NA')
rr[i] = 1 ;
else
rr[i] = (int) strtol(&ranking[i],(char **)NULL,10) ;
}
}
由于 "NA" 值,我将排名设置为字符,但也许还有另一种方法可以做到这一点,使用整数并且在调用函数之前不将 "NA" 替换为 0?
(像这样的代码,总是给我一个零数组...)
您正试图解决几个微妙且重要的问题,尤其是如何使用 R 编译代码以及测试非有限值。
您寻求有关 C 的帮助。我建议您使用 C++——您不需要以复杂的方式使用它。考虑这个包含一个函数的短文件,该函数可以按照您建议的方式处理向量(我只是测试 NA
然后将 42 指定为 simplicit 的标记)或者对值进行平方:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector foo(NumericVector x) {
unsigned int n = x.size();
for (unsigned int i=0; i<n; i++)
if (NumericVector::is_na(x[i]))
x[i] = 42.0;
else
x[i] = pow(x[i], 2);
return x;
}
/*** R
foo( c(1, 3, NA, NaN, 6) )
*/
如果我将其保存在我的盒子上作为 /tmp/foo.cpp
,以便编译、link、加载甚至 运行 嵌入式 R 使用示例,我只需要一行来调用sourceCpp()
:
R> Rcpp::sourceCpp("/tmp/foo.cpp")
R> foo( c(1, 3, NA, NaN, 6))
[1] 1 9 42 42 36
R>
我们可以对整数做同样的事情:
// [[Rcpp::export]]
IntegerVector bar(IntegerVector x) {
unsigned int n = x.size();
for (unsigned int i=0; i<n; i++)
if (IntegerVector::is_na(x[i]))
x[i] = 42;
else
x[i] = pow(x[i], 2);
return x;
}
使用 R_NaInt
测试该值是否为 NA,如
#include <R.h>
void ranking_c(int *nrak, int *ranking, int *rr) {
for (int i=0; i < *nrak; i++)
rr[i] = R_NaInt == ranking[i] ? -1 : ranking[i];
}
通过明确允许 NA 从 R 调用
> x = c(1:2, NA_integer_)
> .C("ranking_c", length(x), as.integer(x), integer(length(x)), NAOK=TRUE)[[3]]
[1] 1 2 -1
或者,使用 R 的 .Call()
接口。每个 R 对象都表示为一个 S 表达式。有一些 C 级函数可以操作 S 表达式,例如长度 Rf_length()
、数据访问 INTEGER()
和分配 Rf_allocVector()
不同类型的 S 表达式,例如用于整数向量的 INTSXP。
R 内存管理使用垃圾收集器,可以 运行 在任何分配内存的调用上。因此,最佳做法是 PROTECT()
在范围内的任何 R 分配。
您的函数将接受 0 个或多个 S 表达式作为输入,并且 return 单个 S 表达式;它可能被实现为
#include <Rinternals.h>
#include <R_ext/Arith.h>
SEXP ranking_call(SEXP ranking)
{
/* allocate space for result, PROTECTing from garbage collection */
SEXP result = PROTECT(Rf_allocVector(INTSXP, Rf_length(ranking)));
/* assign result */
for (int i = 0; i < Rf_length(ranking); ++i)
INTEGER(result)[i] =
R_NaInt == INTEGER(ranking)[i] ? -1 : INTEGER(ranking)[i];
UNPROTECT(1); /* no more need to protect */
return result;
}
并使用 .Call("ranking_call", as.integer(ranking))
从 R 中调用。
在速度和内存分配方面,使用 .Call
比 .C
更有效(.C 可能会在途中复制原子向量),但使用它的主要原因是它在直接使用 R 的数据结构方面提供了灵活性。当 return 值比原子向量更复杂时,这一点尤为重要。