子集化一个向量并对其进行排序

10

我正在研究使用Rcpp包,为我的R包中的一些简单部分使用C++。我是一个C++新手(但很想学习!)。我已经使用优秀的Rcpp实现了一些简单的cpp程序 - 实际上,这个包激励我学习C++...

无论如何,我卡在了一个简单的问题上,如果能解决就会有帮助。我有一个NumericVector,我想对其进行子集划分并排序。下面的代码对整个向量进行排序(并且还会处理NAs,这正是我需要的)。

我的问题是,假设我想提取此向量的一部分,对其进行排序,并使其可用于其他处理 - 我该怎么做?例如,对于长度为10的向量,如何提取和排序元素5:10?

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
RcppExport SEXP rollP(SEXP x) {
  NumericVector A(x); // the data  
  A = sort_unique(A);  
  return A;
}

我从R中调用的函数:

sourceCpp( "rollP.cpp")
rollP(10:1)
# [1]  1  2  3  4  5  6  7  8  9 10

你想把它分成两半吗?还是您希望使用基于R的解决方案或通过C++实现? - pyCthon
我想提取一个连续的范围,例如2:7、5:8或者其他基于提供的起始/结束索引。我将在C++中继续处理,然后将答案返回给R。 - DavidC
4个回答

12

这里有三种变体:

include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector rollP(NumericVector A, int start, int end) {
  NumericVector B(end-start+1) ;
  std::copy( A.begin() + start-1, A.begin() + end, B.begin() ) ;
  return B.sort() ;
}

// [[Rcpp::export]]
NumericVector rollP2(NumericVector A, int start, int end) {
  NumericVector B( A.begin() + start-1, A.begin() + end ) ;
  return B.sort() ;
}

// [[Rcpp::export]]
NumericVector rollP3(NumericVector A, int start, int end) {
  NumericVector B = A[seq(start-1, end-1)] ;
  return B.sort() ;
}

startend被视为基于1的索引,就好像您从R传递A[start:end]一样。


太好了 - 非常有用,谢谢。还有一个后续问题,如果我只想对数据进行排序而不取唯一值,该怎么办? - DavidC
在第二个解决方案中,只需调用 std::sort( B.begin(), B.end() ) 并返回 B。我们将添加一个排序语法糖接口。 - Romain Francois
我编辑了答案,添加了对NumericVector::sort方法的调用。 - Romain Francois
std::partial_sort_copy 也可以使用。 - Artem Klevtsov

4
你需要研究C++的索引、迭代器和整个过程。至少,你需要更改你的接口(vector, fromInd, toInd),并确定你想要返回什么。
对你问题的一个解释是将子集从[fromInd,toInd)复制到新的向量中,对其进行排序并返回。所有这些都是标准的C++内容,像优秀的(而且免费!)C++ Annotations 这样的文本会有所帮助。它还有一个相当强大的STL部分。

谢谢Dirk。我确实怀疑这将是相当标准的东西 - 我必须摸索一下。我感谢您提供的信息,这将帮助我跟进我的问题。非常感谢。 - DavidC

3
你可以在 std::valarray 上使用 std::slice。但如果你想特别使用 std::vector,则可以使用 std::copy 提取向量的一部分,然后使用 std::sort 对提取的向量片段进行排序。

1
这是一个正确的“仅限C ++”答案。而且有了Rcpp,您还可以从SEXP实例化一个C ++向量(例如std::vector<double>),并以此方式进行处理。 - Dirk Eddelbuettel
1
不错。将研究在Rcpp中支持std::slice - Romain Francois

2

您可以通过使用接收两个迭代器的 std::sort 实现来轻松完成此操作:

#include <vector>
#include <cinttypes>
#include <algorithm>

template <typename SeqContainer>
SeqContainer slicesort(SeqContainer const& sq, size_t begin, size_t end) {
  auto const b = std::begin(sq)+begin;
  auto const e = std::begin(sq)+end;
  if (b <= std::end(sq) && e <= std::end(sq)) {
    SeqContainer copy(b,e);
    std::sort(copy.begin(),copy.end());
    return copy;
  }
  return SeqContainer();
}

这可以像下面这样调用:

  std::vector<int> v = {3,1,7,3,6,-2,-8,-7,-1,-4,2,3,9};
  std::vector<int> v2 = slicesort(v,5,10);

不错。但我会将SeqContainer作为引用传递,首先创建SeqContainer,然后对其进行排序。 - Romain Francois
这将对Rcpp向量(例如NumericVector)产生不良影响,因为它们只是数据数组的代理,并且复制构造函数不会复制元素,而是引用底层数据。 - Romain Francois
6
这太过分了。它被这样实施是有原因的。我们不需要在SO上出现那种讽刺的评论。 - Romain Francois
1
@bitmask,你当然知道@romainfrancois是Rcpp的合著者了吧?因此我认为再多点礼貌会产生很长远的效果。 - Andrie
@RomainFrancois:没错。没有人说对象的复制构造必须是一个立即的逐字复制。也许你想要实现一些写时复制机制来加快复制速度。但如果我通过引用传递某些东西,我就会通过引用传递它,而不是通过值——所以如果我复制一个对象,我希望得到一个独立的副本。如果复制对象没有意义,那么就完全隐藏复制构造函数。请注意,我真的不熟悉R,所以这些都是关于容器的一般性观点。无论如何,我应该修改这个答案。 - bitmask
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接