如何根据指数指标告诉C++放弃矢量中的某些元素

how to tell C++ to abandon certain element in vector according to an index indicator

本文关键字:元素 放弃 C++ 指数 何根      更新时间:2023-10-16

我正在使用RCPP来加速项目中的R代码。现在我正在做的是使用犰狳包将我的 R 代码传输到C++。我发现我经常用C++写多行来替换 R 中的一行......

这是我的问题: 我有一个向量存储的数据:数据。我还有一个矩阵,存储了我需要访问的元素索引。请允许我先在 R 中说明我的场景:

> Data
[1] 4 5 6 7 8

> index
[,1] [,2] [,3]
[1,]    1    2    3
[2,]    1    0    0
[3,]    2    0    2

对于"索引"矩阵的每一行,我想从数据中获取相应的元素。在 R 中,我只需要像这样编码:

> Data[index[1,]]
[1] 4 5 6
> Data[index[2,]]
[1] 4
> Data[index[3,]]
[1] 5 5

即 我需要从"索引"矩阵的第一行中的元素是数据[1],数据[2],数据[3]

我需要从"索引"矩阵的第二行中的元素是 数据[1]

我需要从"索引"矩阵的第 3 行获得的元素是 数据[2] 数据[2]

R 的便利之处在于 R 会自动将 0 索引标识为"无"并且不会访问它。

现在我把向量"数据"和矩阵"索引"输入到C中。我想知道有没有办法达到与上述 R 类似的结果?多谢!

Base R

您可以使用索引对数据进行子集化,结果将是一个列表

Data <- c( 4 ,5 ,6, 7, 8)
index <- matrix(c(1,2,3, 1, 0, 0, 2,0,2), byrow = TRUE, nrow = 3)
apply(index, 1, function(x) Data[x])
# [[1]]
# [1] 4 5 6
# 
# [[2]]
# [1] 4
# 
# [[3]]
# [1] 5 5

结果将是矩阵

index <- matrix(c(1,2,3, 1, 0, 0, 2,0,2), byrow = TRUE, nrow = 3)
index[index == 0] <- NA
index
#      [,1] [,2] [,3]
# [1,]    1    2    3
# [2,]    1   NA   NA
# [3,]    2   NA    2
apply(index, 2, function(x) Data[x])
#      [,1] [,2] [,3]
# [1,]    4    5    6
# [2,]    4   NA   NA
# [3,]    5   NA    5

使用[

matrix( Data[index], nrow = 3, byrow = FALSE)   # another way to get the same matrix

Rcpp:对于 0 索引,在Data向量中使用 NA

您只需按照此处所述将apply转换为Rcpp代码

使用[:有关使用 RCpp 进行向量子集的更多信息,请参阅本文

文件:我的子集.cpp

#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector mysubset(NumericVector Data, NumericVector index) {
return Data[index];
}

RStudio:

library('Rcpp')
sourceCpp("mysubset.cpp")
Data <- c( NA, 4 ,5 ,6, 7, 8)  # for 0 index, use NA
index <- matrix(c(1,2,3, 1, 0, 0, 2,0,2), byrow = TRUE, nrow = 3)
matrix( mysubset(Data, index), nrow = 3, byrow = FALSE)
#      [,1] [,2] [,3]
# [1,]    4    5    6
# [2,]    4   NA   NA
# [3,]    5   NA    5
mysubset(Data, index[1,])
# [1] 4 5 6
na.omit(mysubset(Data, index[2,]))
# [1] 4

在C++中,有一些工作要做,但这是可行的:

#include <type_traits>
#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <utility>
template <typename T, typename I,
typename std::enable_if<std::is_convertible<I,
typename std::vector<T>::size_type>::value>::type* = nullptr>
std::vector<std::vector<T>> product (const std::vector<T>& data,
const std::vector<std::vector<I>>& index) {
std::vector<std::vector<T>> result (index.size());
std::transform(std::begin(index),
std::end(index),
std::make_move_iterator(std::begin(result)),
[&data, &filler](const std::vector<I>& index_row) {
std::vector<T> row;
for (auto& pos : index_row) {
if (pos > 0) {
row.push_back(data.at(pos - 1));
}
}
return row;
});
return result;
}

现在演示一下它是如何工作的:

auto main() -> int {
std::vector<int> data = {4, 5, 6, 7, 8};
std::vector<std::vector<int>> index = {
{1, 2, 3},
{1, 0, 0},
{2, 0, 2}
};
std::vector<std::vector<int>> result = std::move(product(data, index));
std::cout << result << "n";
}

输出

4,5,6,
4,
5,5,

演示中使用的帮助程序函数:

template <typename T>
std::ostream& operator << (std::ostream& oss, const std::vector<T>& v) {
for (auto &item : v) {
oss << item << ",";
}
return oss;
}
template <typename T>
std::ostream& operator << (std::ostream& oss, const std::vector<std::vector<T>>& vv) {
for (auto &v : vv) {
oss << v << "n";
}
return oss;
}

如果你想让事情变得简单,那么我推荐以下几点:

假设您有一个数据向量 (Data):

std::vector<int> Data{ 4, 5, 6, 7, 8 };

和一个索引映射,它是列向量的行主序向量(index):

std::vector<std::vector<int>> index{ {1, 2, 3}, {1, 0, 0}, {2, 0, 2} };

那么下面的代码,将取一行index的索引。获取Data的索引元素并将其追加到结果向量,但索引为 0(或越界)除外:

std::vector<int> r;
for (auto i : index[1-1])
if (i > 0 && i <= Data.size())
r.push_back(Data[i-1]);

以下代码的输出

#include <vector>
#include <iostream>
std::vector<int> Data{ 4, 5, 6, 7, 8 };
std::vector<std::vector<int>> index{ {1, 2, 3}, {1, 0, 0}, {2, 0, 2} };
std::vector<int> r1, r2, r3;
for (auto i : index[1-1]) if (i > 0 && i <= Data.size()) r1.push_back(Data[i-1]);
for (auto i : index[2-1]) if (i > 0 && i <= Data.size()) r2.push_back(Data[i-1]);
for (auto i : index[3-1]) if (i > 0 && i <= Data.size()) r3.push_back(Data[i-1]);
for (auto d : r1) std::cout << d << " ";  std::cout << std::endl;
for (auto d : r2) std::cout << d << " ";  std::cout << std::endl;
for (auto d : r3) std::cout << d << " ";  std::cout << std::endl;

是:

4 5 6
4
5 5


从理论上讲,您需要一种算法,例如std::transform_if.但这并不存在。请参阅为什么C++标准库中没有transform_if?