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 );
}
假设我有一个向量 [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 );
}