有没有简单的方法可以通过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代码的速度)。
非常感谢
答案 0 :(得分: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 ;
} ;
然后,对于我们在此file中支持的所有类型, dplyr都有OrderVisitor
的实现,并且我们有一个调度函数order_visitor
,它从向量中生成OrderVisitor*
。
有了这个,我们可以将一组矢量访问者存储到std::vector<OrderVisitor*>
; OrderVisitors有一个构造函数,它使用DataFrame
和CharacterVector
个我们想要用于排序的向量名称。
OrderVisitors o(data, names ) ;
然后我们可以使用基本上执行词典排序的OrderVisitors.apply
method:
IntegerVector index = o.apply() ;
apply
方法是通过简单地根据访问者初始化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 ;
}
这里的相关内容是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
为我们提供了排序数据的整数索引,我们只需要通过使用这些索引对DataFrame
进行子集来从data
创建一个新的data
。为此我们有另一种访问者,封装在DataFrameVisitors
类中。我们首先创建一个DataFrameVisitors
:
DataFrameVisitors visitors( data ) ;
这封装了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 ;
}
为了解决这个问题,这里有一个简单的函数,使用在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 ;
}
答案 1 :(得分:3)
因为data.frame
实际上是C ++中的列列表,所以在给定新的ording索引的情况下,您必须单独重新排序所有列。这与[.., ..]
中的data.frame
索引在R中的工作方式不同。
参见例如this Rcpp Gallery article on sorting vectors有些指示。 您可能必须提供要使用的新排序索引,之后它只是一个索引问题 - 而且在Gallery上也有一些帖子。
This SO post可以帮助您开始创建索引; this bytes.com post讨论了同样的想法。
修改:并Armadillo has function sort_index()
和stable_sort_index()
创建重新排列列所需的索引。这仅涵盖一列情况,并且仅限于数字列,但它是一个开始。