困难在于数据框是一组向量,可能是不同类型的;我们需要一种方法来独立于这些类型(整数、字符、...)对它们进行排序。在 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 有一个构造函数,其中包含我们要用于排序的向量名称的 DataFrame 和 CharacterVector。
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<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 ;
}