如何提高在 R 中聚合和汇总多个变量的速度?

How to increase the speed of aggregating and summarizing multiple variables in R?

我正在执行一个重采样(即 bootstrap)过程,作为重复的步骤之一,该过程涉及计算多个组中每个组的多个数字变量的平均值。我发现使用 dplyrdoBydata.table 的解决方案非常简单,我在下面提供了这些解决方案。

但是,它们中的每一个通常需要超过一毫秒才能完成(根据 microbenchmark)。由于这个过程会重复几千次(连同其他操作),我想尽可能地优化它。理想情况下,它会在几微秒或更快的时间内完成。

谁能想办法提高这些操作的速度?我的一个想法是将数字变量转换为矩阵并使用 colMeans(),但不确定如何快速进行子集化。

dat <- data.frame(
  a = runif(1000),
  b = runif(1000),
  c = runif(1000),
  group = factor(rep(c(1, 2), 500))
)

library(dplyr)
dat %>% group_by(group) %>% summarise_all(mean)
#microbenchmark = 7.1 milliseconds

library(doBy)
summaryBy(. ~ group, dat, FUN = mean)
#microbenchmark = 4.6 milliseconds

library(data.table)
setDT(dat)[, lapply(.SD, mean), by = 'group']
#microbenchmark = 1.8 milliseconds

#base
mat <- as.matrix(dat[, 1:(ncol(dat) - 1)])
grp <- dat$group
by(mat, grp, colMeans)
#microbenchmark = 1.2 milliseconds

更新:

为了提供有关我的更广泛任务的更多信息,我正在创建一个函数,该函数将接收来自 g 个互斥组中 n 个主题的 k 个变量的数据数据框的形式 (n-by-k)。该函数的主要目的是首先通过获取每个组内每个变量的平均值 (g-by-k) 来汇总数据,然后将统计函数分别应用于每个组的均值向量 (1-by-k ).此统计函数 returns p 估计感兴趣的参数。

此外,需要计算这些估计值的 bootstrapped 置信区间,因此该函数为每个 r 重采样估计这些参数,并从原始数据框(按组分层)进行替换.最终,我需要知道每个重采样 (p-by-g-by-r) 中每个组的参数估计值,以便我可以使用百分位数或其他一些方法来估计每个组中每个参数的置信区间。

请注意,我已经成功优化了统计功能,现在在最常见的向量大小上完成该功能大约需要 50 微秒。因此,剩下的瓶颈似乎是为每个重采样创建这些向量(即聚合和总结)。

我能够使用 Rcpp 和 RcppArmadillo 达到微秒的数量级。

dat <- data.frame(
  a = runif(1000),
  b = runif(1000),
  c = runif(1000),
  group = factor(rep(c(1, 2), 500))
)
mat <- as.matrix(dat[, 1:(ncol(dat) - 1)])
grp <- as.integer(dat$group)
group_scores(mat, grp)
#microbenchmark: 48 microseconds

下面是 group_scores 函数的 Rcpp 代码:

# include <RcppArmadillo.h>
# include <RcppArmadilloExtensions/sample.h>
// [[Rcpp::depends(RcppArmadillo)]]

using namespace Rcpp;

//[[Rcpp::export]]
arma::mat submat(NumericMatrix X, NumericVector T, int TestVal) {
  arma::mat Xmat(X.begin(), X.nrow(), X.ncol(), false);
  arma::colvec tIdx(T.begin(), T.size(), false); 
  arma::mat y = Xmat.rows(find(tIdx == TestVal));
  return y;
}

// [[Rcpp::export]]
arma::rowvec col_means(arma::mat x){
  arma::mat X = arma::mat(x.begin(), x.n_rows, x.n_cols, false); 
  return arma::mean(X, 0); 
}

//[[Rcpp::export]]
arma::mat group_scores(NumericMatrix X, NumericVector T) {
  NumericVector levels = unique(T);
  int n = levels.size();
  int m = X.ncol();
  arma::mat out(n, m);
  for (int i(0); i < n; i++) {
    int level = levels(i);
    arma::mat sub = submat(X, T, level);
    arma::rowvec colmeans = col_means(sub);
    out.row(i) = colmeans;
  }
  return out;
}