【问题标题】:passing in R array to C function with "NA"使用“NA”将 R 数组传递给 C 函数
【发布时间】:2016-02-25 21:21:36
【问题描述】:

我使用 C 库在 R 中工作。我需要将一个数字在 1 到 10 之间的数组传递给 C 函数,但这也可能是“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?

(像这样的代码,总是给我一个零数组...)

【问题讨论】:

  • 不要像NULL 宏一样强制转换空指针常量。一般规则是不要使用不必要的演员表。
  • 好的。感谢!但我认为这不能解决问题
  • 评论不是为了回答。
  • 无论如何,你的意思是我应该简单地做(int) strtol(&amp;rating[i],NULL,10)
  • 当然!请注意,您确保使用 C 编译器。尝试使用 C++ 编译器编译 C 代码通常会导致麻烦。相同的语法并不意味着相同的语义。

标签: c r parameter-passing dynamic-library


【解决方案1】:

使用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() 以及分配不同类型的 S 表达式的 Rf_allocVector(),例如整数向量的 INTSXP。

R 内存管理使用可以在任何分配内存的调用上运行的垃圾收集器。因此,最佳实践是在范围内PROTECT() 任何 R 分配。

您的函数将接受 0 个或多个 S-表达式作为输入,并返回单个 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 的数据结构。当返回值比原子向量更复杂时,这一点尤其重要。

【讨论】:

  • 非常感谢!我想我首先需要更深入地研究 R 中有关动态 C 库的命令、规则和命名法。顺便说一句,就效率和内存消耗而言,这种方法是否比使用 .C 函数和避免 S 表达式更好?跨度>
  • 不用说,Rcpp 也是 a) 使用 .Call(),b) 保护您免受所有 PROTECT()UNPROTECT() 线路噪音的影响,因为它会在幕后为您完成所有这些工作,c)添加异常处理,d) 为您提供构建工具,....但有些人真的更喜欢 C 并做所有额外的工作,这完全没问题。正如加缪所说,我们也将西西弗斯想象成一个快乐的人......
【解决方案2】:

您正在尝试解决一些微妙且重要的问题,尤其是如何使用 R 编译代码以及测试非有限值。

您在 C 方面寻求帮助。我想推荐 C++——您不需要以复杂的方式使用它。考虑这个短文件,其中包含一个函数,可以按照您建议的方式处理向量(我只是测试NA,然后将 42 分配为简单的标记)或者将值平方:

#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,为了编译、链接、加载甚至运行嵌入式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;
}

【讨论】:

  • 总是能获得 立即 反对票,而 当然 缺少任何 cmets。很棒的东西。
  • 我实际上并没有对你投反对票!我能怎么做!你给了我很大的帮助!!我会努力学习和理解它(也许明天,这里已经快午夜了......)。我相信它不仅会帮助我解决这个问题!非常感谢!
  • @dirk 偏好使用 Rcpp 语法糖 **Vector::is_na(x[i]) 与不使用糖 IntegerVector[i] == NA_INTEGER 的理由是什么?我倾向于发现使用 Sugar 时调试更复杂。
  • 如果我的数学是正确的,是不是建议“C++ --”和建议C一样?
  • 无法反驳,@JoshuaUlrich!你看我真的是一个有爱心的C程序员......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-14
  • 2013-06-26
  • 2014-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多