在Rcpp中按列排序数据帧

uda*_*day 8 r rcpp

是否有任何简单的方法可以通过RCpp中的两个(或多个或一个)列来订购DataFrame?

网上有很多排序算法,或者我可以使用std::sortDataFrame的包装器,但我想知道RCpp或RCppArmadillo中是否有可用的东西?

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

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

我想避免order在RCpp中访问R的功能(为了保持RCpp代码的速度).

非常感谢

Rom*_*ois 12

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

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 ;

} ;
Run Code Online (Sandbox Code Playgroud)

然后,dplyr具有OrderVisitor我们在此文件中支持的所有类型的实现,并且我们有一个调度函数order_visitor,它OrderVisitor*从一个向量中生成一个.

有了这个,我们可以将一组矢量访问者存储到一个std::vector<OrderVisitor*>; 该OrderVisitors有一个构造采取DataFrameCharacterVector我们要使用的排序向量的名字.

OrderVisitors o(data, names ) ;
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用基本上执行词典排序的OrderVisitors.apply方法:

IntegerVector index = o.apply() ;
Run Code Online (Sandbox Code Playgroud)

apply方法通过简单地初始化a IntegerVector,0..n然后std::sort根据访问者来实现.

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

这里的相关内容是OrderVisitors_Compare类如何实现operator()(int,int):

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 ;
}
Run Code Online (Sandbox Code Playgroud)

所以在这一点index给我们的排序数据的整数索引值,我们只需要创建一个新DataFramedata由子集划分data与这些指标.为此我们有另一种访问者,封装在DataFrameVisitors课堂上.我们先创建一个DataFrameVisitors:

DataFrameVisitors visitors( data ) ;
Run Code Online (Sandbox Code Playgroud)

这封装了一个std::vector<VectorVisitor*>.这些中的每一个都VectorVisitor*知道如何使用整数向量索引来子集自身.这用于DataFrameVisitors.subset:

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 ;
}
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,这里有一个简单的函数,使用在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 ;  
}
Run Code Online (Sandbox Code Playgroud)


Dir*_*tel 3

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

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

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

编辑:犰狳具有创建重新排列列所需的索引的功能。 这仅涵盖一列的情况,并且仅限于数字列,但这是一个开始。sort_index()stable_sort_index()