提问者:小点点

在Rcpp中按列排序数据帧


在RCPP中,有没有任何简单的方法按数据帧的两个(或多个或一个)列对数据帧进行排序?

网络上有许多可用的排序算法,或者我可以使用和数据帧的包装器,但是我想知道RCpp或RCPParmadillo中是否已经有可用的算法?

我需要将此排序/排序作为另一个函数的一部分

DataFrame myFunc(DataFrame myDF, NumericVector x) {
  //// some code here
  DataFrame myDFsorted = sort (myDF, someColName1, someColName2) // how to sort??
  //// some code here
}

我希望避免访问RCpp中的R's函数(以保持RCpp代码的速度)。

非常感谢


共2个答案

匿名用户

困难在于数据帧是一组可能具有不同类型的向量;我们需要一种方法来独立于这些类型(整数,字符,。。。。)对它们进行排序。在dplyr中,我们开发了我们所说的向量访问者。对于这个特定的问题,我们需要的是一组,它们展示了以下接口:

class OrderVisitor {
public:
    virtual ~OrderVisitor(){}

    /** are the elements at indices i and j equal */
    virtual bool equal(int i, int j) const  = 0 ;

    /** is the i element less than the j element */
    virtual bool before( int i, int j) const = 0 ;

    virtual SEXP get() = 0 ;

} ;

然后,dplyr为我们在此文件中支持的所有类型提供了的实现,并且我们有一个调度函数,它从一个向量生成一个

有了这个,我们可以将一组向量访问者存储到,它们是我们想要用于排序的向量的名称。

OrderVisitors o(data, names ) ;

然后,我们可以使用方法,该方法实质上执行词典排序:

IntegerVector index = o.apply() ;

方法是通过简单地用初始化,然后根据访问者初始化来实现的。

inline Rcpp::IntegerVector OrderVisitors::apply() const {
    IntegerVector x = seq(0, nrows -1 ) ;
    std::sort( x.begin(), x.end(), OrderVisitors_Compare(*this) ) ;
    return x ;
}

这里的相关内容是类如何实现:

inline bool operator()(int i, int j) const {
    if( i == j ) return false ;
    for( int k=0; k<n; k++)
        if( ! obj.visitors[k]->equal(i,j) )
            return obj.visitors[k]->before(i, j ) ; 
    return i < j ;
}

因此,此时给出了排序数据的整数索引,我们只需用这些索引子集,从生成一个新的。为此,我们有另一种类型的访问者,封装在类中。我们首先创建一个:

DataFrameVisitors visitors( data ) ;

这封装了一个。这些中的每一个都知道如何用整数向量索引子集自己。这是从中使用的:

template <typename Container>
DataFrame subset( const Container& index, const CharacterVector& classes ) const {
    List out(nvisitors);
    for( int k=0; k<nvisitors; k++){
       out[k] = get(k)->subset(index) ;    
    }
    structure( out, Rf_length(out[0]) , classes) ;
    return (SEXP)out ;
}

为了总结这一点,下面是一个使用DPLYR中开发的工具的简单函数:

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

using namespace Rcpp ;
using namespace dplyr ;

// [[Rcpp::export]]
DataFrame myFunc(DataFrame data, CharacterVector names) {
  OrderVisitors o(data, names ) ;
  IntegerVector index = o.apply() ;

  DataFrameVisitors visitors( data ) ;
  DataFrame res = visitors.subset(index, "data.frame" ) ;
  return res ;  
}

匿名用户

因为在C++中实际上是列的列表,所以在给定新的排序索引的情况下,您必须单独重新排序所有列。这与索引在R中的工作方式不同。

例如,请参阅Rcpp画廊关于排序向量的文章,以获得一些指针。您可能必须提供要使用的新的排序索引,然后它只是一个索引问题--这也有一些帖子在画廊上。

这篇SO文章可能会让您开始创建索引;Bytes.com的这篇文章讨论了同样的想法。

编辑:Armadillo有函数来创建重新排列列所需的索引。这只涵盖了一个列的情况,并且仅限于数字列,但这是一个开始。