如何在C++/Rcpp中进行快速百分位数计算

Alv*_*vin 7 c++ r armadillo rcpp

我有一个包含一堆双元素的大向量.给定一个百分位向量的数组,例如percentile_vec = c(0.90, 0.91, 0.92, 0.93, 0.94, 0.95).我目前正在使用Rcpp sort函数对大向量进行排序,然后找到相应的百分位值.这是主要代码:

// [[Rcpp::export]]
NumericVector sort_rcpp(Rcpp::NumericVector& x)
{
  std::vector<double> tmp = Rcpp::as<std::vector<double>> (x);    // or NumericVector tmp = clone(x);
  std::sort(tmp.begin(), tmp.end());
  return wrap(tmp);
}

// [[Rcpp::export]]
NumericVector percentile_rcpp(Rcpp::NumericVector& x, Rcpp::NumericVector& percentile)
{
  NumericVector tmp_sort = sort_rcpp(x);
  int size_per = percentile.size();
  NumericVector percentile_vec = no_init(size_per);
  for (int ii = 0; ii < size_per; ii++)
  {
    double size_per = tmp_sort.size() * percentile[ii];
    double size_per_round;
    if (size_per < 1.0)
    {
      size_per_round = 1.0;
    }
    else
    {
      size_per_round = std::round(size_per);
    }
    percentile_vec[ii] = tmp_sort[size_per_round-1];  // For extreme case such as size_per_round == tmp_sort.size() to avoid overflow
  }
  return percentile_vec;
}
Run Code Online (Sandbox Code Playgroud)

我还尝试quantile(x, c(.90, .91, .92, .93, .94, .95))使用以下命令在Rcpp中调用R函数:

sub_percentile <- function (x)
{
  return (quantile(x, c(.90, .91, .92, .93, .94, .95)));
}  

source('C:/Users/~Call_R_function.R')
Run Code Online (Sandbox Code Playgroud)

测试结果x=runif(1E6)如下:

microbenchmark(sub_percentile(x)->aa, percentile_rcpp(x, c(.90, .91, .92, .93, .94, .95))->bb)
#Unit: milliseconds
              expr      min       lq     mean   median       uq       max   neval
  sub_percentile(x) 99.00029 99.24160 99.35339 99.32162 99.41869 100.57160   100
 percentile_rcpp(~) 87.13393 87.30904 87.44847 87.40826 87.51547  88.41893   100
Run Code Online (Sandbox Code Playgroud)

我期待一个快速百分位计算,但我认为std::sort(tmp.begin(), tmp.end())减慢了速度.有没有更好的方法来使用C++,RCpp/RcppAramdillo获得快速结果?谢谢.

ren*_*nsz 1

循环中的分支肯定可以得到优化。使用带有整数的 std::min/max 调用。

我会这样解决数组索引的百分比计算:

uint PerCentIndex( double pc, uint size )
{
    return 0.5 + ( double ) ( size - 1 ) * pc;
}
Run Code Online (Sandbox Code Playgroud)

只有上面循环中间的这一行:

percentile_vec[ii] 
 = tmp_sort[ PerCentIndex( percentile[ii], tmp_sort.size() ) ];
Run Code Online (Sandbox Code Playgroud)