【问题标题】:order a dataframe by column in Rcpp在 Rcpp 中按列排序数据帧
【发布时间】:2014-06-01 04:36:47
【问题描述】:

是否有任何简单的方法可以通过 RCpp 中的两列(或多列或一列)对 DataFrame 进行排序?

网上有很多可用的排序算法,或者我可以使用 std::sort 和 DataFrame 的包装器,但我想知道 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 的 order 函数(以保持 RCpp 代码的速度)。

非常感谢

【问题讨论】:

    标签: r rcpp


    【解决方案1】:

    困难在于数据框是一组向量,可能是不同类型的;我们需要一种方法来独立于这些类型(整数、字符、...)对它们进行排序。在 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 ;
    
    } ;
    

    dplyr 然后为我们在这个file 中支持的所有类型实现了OrderVisitor,我们有一个调度函数order_visitor,它从一个向量生成一个OrderVisitor*

    这样,我们可以将一组矢量访问者存储到std::vector<OrderVisitor*>OrderVisitors 有一个构造函数,其中包含我们要用于排序的向量名称的 DataFrameCharacterVector

    OrderVisitors o(data, names ) ;
    

    然后我们可以使用OrderVisitors.apply method,它本质上是按字典顺序排列的:

    IntegerVector index = o.apply() ;
    

    apply 方法是通过简单地用0..n 初始化一个IntegerVector 来实现的,然后根据访问者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 ;
    }
    

    这里相关的是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 ;
    }
    

    所以此时index 为我们提供了已排序数据的整数索引,我们只需通过使用这些索引对data 进行子集化,从data 创建一个新的DataFrame。为此,我们有另一种访问者,封装在 DataFrameVisitors 类中。我们首先创建一个DataFrameVisitors

    DataFrameVisitors visitors( data ) ;
    

    这封装了一个std::vector&lt;VectorVisitor*&gt;。这些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 ;
    }
    

    总结一下,这里有一个使用 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 ;  
    }
    

    【讨论】:

    • 你能不能逐行解释myFunc
    • @RomainFrancois,使用sourceCpp 可以很好地编译代码,但是每次调用myFunc 时,RStudio 都会崩溃。不知道为什么。
    • @uday 我认为这是由this commit 修复的。 Gabor,我将扩展这个答案。
    • @G.Grothendieck 我希望他能给你足够的细节。
    • @RomainFrancois,dplyr 的新版本发布了吗?
    【解决方案2】:

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

    参见例如this Rcpp Gallery article on sorting vectors 一些指针。 您可能必须提供要使用的新排序索引,之后它只是一个索引问题 - 在图库中也有一些帖子。

    This SO post 可以帮助您开始创建索引; this bytes.com post 讨论了同样的想法。

    编辑:Armadillo has function sort_index()stable_sort_index() 创建重新排列列所需的索引。这仅涵盖单列情况,并且仅限于数字列,但只是一个开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-20
      • 2020-05-12
      • 1970-01-01
      • 1970-01-01
      • 2020-04-25
      • 2021-01-05
      相关资源
      最近更新 更多