【问题标题】:equivalent of 'which' function in Rcpp相当于 Rcpp 中的 'which' 函数
【发布时间】:2014-05-24 20:28:52
【问题描述】:

我是 C++ 和 Rcpp 的新手。假设,我有一个向量

t1<-c(1,2,NA,NA,3,4,1,NA,5)

我想获得 t1 元素的索引 NA。我会写:

NumericVector retIdxNA(NumericVector x) {

    // Step 1: get the positions of NA in the vector
    LogicalVector y=is_na(x);

    // Step 2: count the number of NA
    int Cnt=0;
    for (int i=0;i<x.size();i++) {
       if (y[i]) {
         Cnt++;
       }
    }

    // Step 3: create an output matrix whose size is same as that of NA
    // and return the answer
    NumericVector retIdx(Cnt);
    int Cnt1=0;
    for (int i=0;i<x.size();i++) {
       if (y[i]) {
          retIdx[Cnt1]=i+1;
          Cnt1++;
       }
    }
    return retIdx;
}

然后我得到

retIdxNA(t1)
[1] 3 4 8

我想知道:

(i) Rcpp 中是否有任何与which 等效的东西?

(ii) 有什么方法可以使上述功能更短/更清晰?特别是,有没有什么简单的方法可以把上面的第1、2、3步结合起来?

【问题讨论】:

标签: r rcpp


【解决方案1】:

RcppArmadillo 的最新版本具有识别有限和非有限值索引的功能。

所以这段代码

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
arma::uvec whichNA(arma::vec x) {
  return arma::find_nonfinite(x);
}

/*** R
t1 <- c(1,2,NA,NA,3,4,1,NA,5)
whichNA(t1)
*/

产生您想要的答案(在 C/C++ 中将它们逐一模块化,因为它们是从零开始的):

R> sourceCpp("/tmp/uday.cpp")

R> t1 <- c(1,2,NA,NA,3,4,1,NA,5)

R> whichNA(t1)
     [,1]
[1,]    2
[2,]    3
[3,]    7
R> 

如果您首先创建要子集化的序列,Rcpp 也可以这样做:

// [[Rcpp::export]]
Rcpp::IntegerVector which2(Rcpp::NumericVector x) {
  Rcpp::IntegerVector v = Rcpp::seq(0, x.size()-1);
  return v[Rcpp::is_na(x)];
}

添加到上面的代码会产生:

R> which2(t1)
[1] 2 3 7
R> 

逻辑子集在 Rcpp 中也有些新。

【讨论】:

    【解决方案2】:

    试试这个:

    #include <Rcpp.h> 
    using namespace Rcpp; 
    
    // [[Rcpp::export]]
    IntegerVector which4( NumericVector x) {
    
        int nx = x.size();
        std::vector<int> y;
        y.reserve(nx);
    
        for(int i = 0; i < nx; i++) {
            if (R_IsNA(x[i])) y.push_back(i+1);
        }
    
        return wrap(y);
    }
    

    我们可以在 R 中这样运行:

    > which4(t1)
    [1] 3 4 8
    

    性能

    请注意,我们更改了上述解决方案,为输出向量保留空间。这将替换 which3,即:

    // [[Rcpp::export]]
    IntegerVector which3( NumericVector x) {
        int nx = x.size();
        IntegerVector y;
        for(int i = 0; i < nx; i++) {
            // if (internal::Rcpp_IsNA(x[i])) y.push_back(i+1);
            if (R_IsNA(x[i])) y.push_back(i+1);
        }
        return y;
    }
    

    那么在 9 个元素长的向量上的性能如下,which4 最快:

    > library(rbenchmark)
    > benchmark(retIdxNA(t1), whichNA(t1), which2(t1), which3(t1), which4(t1), 
    +    replications = 10000, order = "relative")[1:4]
              test replications elapsed relative
    5   which4(t1)        10000    0.14    1.000
    4   which3(t1)        10000    0.16    1.143
    1 retIdxNA(t1)        10000    0.17    1.214
    2  whichNA(t1)        10000    0.17    1.214
    3   which2(t1)        10000    0.25    1.786
    

    对长度为 9000 个元素的向量重复此操作,Armadillo 解决方案的速度比其他解决方案快很多。这里which3(与which4 相同,只是它不为输出向量保留空间)最差,而which4 排在第二位。

    > tt <- rep(t1, 1000)
    > benchmark(retIdxNA(tt), whichNA(tt), which2(tt), which3(tt), which4(tt), 
    +   replications = 1000, order = "relative")[1:4]
              test replications elapsed relative
    2  whichNA(tt)         1000    0.09    1.000
    5   which4(tt)         1000    0.79    8.778
    3   which2(tt)         1000    1.03   11.444
    1 retIdxNA(tt)         1000    1.19   13.222
    4   which3(tt)         1000   23.58  262.000
    

    【讨论】:

    • 当然可以,但是为什么不使用我发布的非循环二线呢?如果您认为这很重要,可以在序列中随意添加 +1 偏移量。
    • 为了紧凑性和可读性,您的解决方案之一会更好,但如果速度是一个问题,那么我会怀疑这个解决方案更快,因为通常情况下 Rcpp 使用去矢量化代码运行得更快。它也更接近海报的解决方案。请注意,我确实给了你一票。
    • 如果你测量了呢?
    • 好的。添加了它们。 which4 在小向量的速度基础上获胜,whichNA 在大向量的基础上获胜。
    • @Dirk,顺便说一句,最初我在which4 中省略了wrap,但编译失败。为什么没有wrap 不自动转换返回值?将reserve 方法添加到NumericVector 是否可行,因为它似乎对长向量产生了巨大的影响?
    【解决方案3】:

    以上所有解决方案都是串行的。虽然不是微不足道的,但很有可能利用线程来实现which。有关详细信息,请参阅this write up。虽然对于这么小的尺寸,它不会弊大于利。就像坐飞机走一小段路一样,你在机场安检上浪费了太多时间..

    R 通过为与输入一样大的逻辑向量分配内存来实现which,执行单次传递以将索引存储在此内存中,然后最终将其复制到适当的逻辑向量中。

    直观地说,这似乎比双循环循环效率低,但不一定,因为复制数据范围很便宜。查看更多详情here

    【讨论】:

    • 在某处。 github.com/romainfrancois/Rcpp-book/tree/master/chapters/… 代码可能需要刷新,因为它已经 4 个月大了。但是请注意,它需要 &lt;thread&gt; 标头,AFAIK 在当前 Rtools Windows 工具集上不可用 ..
    • 这一切都很好,您可能会喜欢keynote Luke gave at our conference,因为“在某些时候”会有基于 R 中 OpenMP 的类似方法,但也与 OP 要求的完全正交:a今天可以使用的Rcpp(不是Rcpp11)的解决方案,
    • 您今天可以使用 Rcpp11。
    • 今天您也可以在 Rcpp 中使用 C++11。但是您仍然没有给 OP 一个他可以使用的答案。
    • @DirkEddelbuettel,Rcpp 和 Rcpp11 的主要区别是什么?
    【解决方案4】:

    只需为自己编写一个函数,例如:

    which_1<-function(a,b){
    return(which(a>b))
    }
    

    然后把这个函数传入rcpp。

    【讨论】:

    • 欢迎来到stackoverflow :)
    猜你喜欢
    • 2012-06-27
    • 2012-08-25
    • 1970-01-01
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 2010-09-23
    • 2021-08-20
    • 1970-01-01
    相关资源
    最近更新 更多