Rcpp/RcppArmadillo:根据位置从向量中删除不连续的元素

Rcpp/RcppArmadillo: removing non-contiguous elements from a vector based on positions

假设我有一个向量 [2,4,6,8,10],我需要从该向量中删除第 2 个 第 4 个元素。所需的结果向量应为 [2,6,10]。这在 R 中很容易实现:

v1 <- c(2,4,6,8,10)
v1[-c(2,4)]

但是我如何在 Rcpp/RcppArmadillo 中实现它?我可以通过使用 .erase() 函数找出连续的情况(即删除第二个 第 4 个元素),但非连续的情况似乎并不那么明显我因为 .erase 似乎不接受 uvec 类型的向量。速度可能是一个考虑因素,因为 v1 在我的应用程序中可能非常大。

编辑:Rcpp 或 Armadillo 实现对我来说都很好,因为我正在使用两者。

这是一种可能的方法:

#include <Rcpp.h>

Rcpp::LogicalVector logical_index(Rcpp::IntegerVector idx, R_xlen_t n) {
  bool invert = false; 
  Rcpp::LogicalVector result(n, false);

  for (R_xlen_t i = 0; i < idx.size(); i++) {
    if (!invert && idx[i] < 0) invert = true;
    result[std::abs(idx[i])] = true;
  }

  if (!invert) return result;
  return !result;
}


// [[Rcpp::export]]
Rcpp::NumericVector 
Subset(Rcpp::NumericVector x, Rcpp::IntegerVector idx) {
  return x[logical_index(idx, x.size())];
}

x <- seq(2, 10, 2)

x[c(2, 4)]
#[1] 4 8
Subset(x, c(1, 3))
#[1] 4 8

x[-c(2, 4)]
#[1]  2  6 10
Subset(x, -c(1, 3))
#[1]  2  6 10 

请注意,Rcpp 函数的索引是从 0 开始的,因为它们是在 C++ 中处理的。

我把子集逻辑抽象成它自己的函数,logical_index,它把一个IntegerVector转换成一个LogicalVector,以便能够"decide"是否丢弃或保留指定的元素(例如通过反转结果)。我想这也可以通过 integer-based 子集来完成,但无论哪种方式都不重要。

就像R中的向量子集化一样,所有负索引的向量意味着删除相应的元素;而所有正索引的向量表示要保留的元素。我没有检查混合情况,这可能会引发异常,就像 R 会做的那样。


关于我最后的评论,可能更明智的做法是依赖 Rcpp 的原生重载进行普通子集化,并具有用于否定子集化的专用函数(R 的 x[-c(...)] 构造),而不是像上面那样混合功能.有 pre-existing 个用于创建此类函数的糖表达式,例如

#include <Rcpp.h>

template <int RTYPE>
inline Rcpp::Vector<RTYPE> 
anti_subset(const Rcpp::Vector<RTYPE>& x, Rcpp::IntegerVector idx) {
  Rcpp::IntegerVector xi = Rcpp::seq(0, x.size() - 1);
  return x[Rcpp::setdiff(xi, idx)];
}

// [[Rcpp::export]]
Rcpp::NumericVector 
AntiSubset(Rcpp::NumericVector x, Rcpp::IntegerVector idx) {
  return anti_subset(x, idx);
}

/*** R

x <- seq(2, 10, 2)

x[-c(2, 4)]
#[1]  2  6 10

AntiSubset(x, c(1, 3))
#[1]  2  6 10

*/ 

这是我编写的用于执行此任务的函数。不使用负索引,而是通过函数调用。它比我的基准(小向量)上的 R 函数慢一点。也许有人可以在此基础上构建,我还没有测试过 nrussel 的代码,所以这可能是次等的。编辑 - 如果您要传递包含要删除的索引的 R 向量,请将 if 语句从 "inds(j) == i" 更改为 "inds(j)-1 == i"(我相信)。

注意 - 可以通过根据找到的索引对内循环设置下限来提高性能。当然假设索引向量按升序排序。

arma::uvec rmIndices( unsigned int vecsize, arma::uvec inds){
  unsigned int negInds = 0, p = inds.size();
  bool foundMatch = false;
  arma::uvec neg_inds(vecsize - p);

  for(unsigned int i = 0; i < vecsize; ++i){

    for(unsigned int j = 0; j < p; ++j){
      if(inds(j) == i){//Check if we have a match between the index and the specified value
        foundMatch = true;
      }
    }//End inner loop
    if(!foundMatch){
      neg_inds(negInds) = i;
      negInds = negInds + 1;//We have a match so, go to next position.
    }
    foundMatch = false;
  }

  return( neg_inds );
}