【问题标题】:How to use statistics to speed up row-wise computations on a data.frame?如何使用统计数据来加速 data.frame 上的逐行计算?
【发布时间】:2021-09-04 05:09:05
【问题描述】:

我有一个包含 10,000 行和 40 列的数据框。我正在尝试对这些行中的每一行应用一个函数。对于每一行,我期望返回一个标量,它是我在这个函数中计算的统计数据的值。以下是我到目前为止所做的;

library(sandwich)

# Creating example data #

nrows=10000
ncols=40
n1=20
n2=20
df=data.frame(t(replicate(nrows, rnorm(ncols, 100, 3))))
cov=data.frame(group=as.factor(rep(c(1,2),c(n1,n2))))

# Function to evaluate on each row of df #

get_est= function(x){
mod = lm(x~cov$group)
vcov = vcovHC(mod)
coef = as.numeric(mod$coefficients[2])
se = sqrt(as.numeric(diag(vcov)[2]))
stats = coef/se
return(stats)
}

# Applying above function to full data #

t1=Sys.time()
estimates=apply(df, 1, function(x) get_est(x))
t2=Sys.time()-t1

# Time taken by apply function

Time difference of 32.10623 secs

有没有办法显着减少对完整数据实施 get_est() 的时间?我需要在单个 df 上加快计算速度的主要原因是因为我还有 1000 个具有相同维度的数据帧,并且我必须将此函数同时应用于每个数据帧的每一行。为了说明,下面是我正在处理的更广泛的情况;

# Creating example data

set.seed(1234)
nrows = 10000
ncols = 40
n1 = 20
n2 = 20
df.list = list()
for(i in 1:1000){
  df.list[[i]] = data.frame(t(replicate(nrows, rnorm(ncols, 100, 3))))
}

# Applying get_est() to each row and to each of data frame in df.list #

all.est = foreach(j = 1:length(df.list), .combine = cbind, .packages = 'sandwich') %dopar% {
  cov=data.frame(group=as.factor(rep(c(1,2),c(n1,n2))))
  est = apply(df.list[[j]], 1, function(x) get_est(x))
  return(est)
}

即使在并行化之后也需要数小时才能完成。我的最终目标是显着缩短获取“all.est”的时间,该“all.est”将包含 10000 行和 1000 列,其中每列都有相应数据集的统计估计值。任何帮助深表感谢!!提前致谢!

【问题讨论】:

  • 更适合stackoverflow。
  • 请参阅 stats.stackexchange.com/help/on-topic 以明确要求不要在 SE 上交叉发布。
  • 下面关于预计算部分结果的答案依赖于算法的细节,并且需要统计专业知识才能得出。将此与 SE 上关于并行化的答案进行比较,后者没有并且提供相对较小的性能改进,很明显,这个问题最好在这里回答。我投票决定重新开放。
  • 非常感谢@ChrisHaug 的支持!! @Nick Cox 感谢您让我知道指导方针,在以后发布任何内容之前,我一定会牢记这一点。
  • 嗨@Capri,我看到您对帖子进行了编辑,导致答案无效。这对网站非常有害!使答案无效意味着那些花费时间和精力来理解和回答您的问题的人的工作被否定了。当您意识到您的问题并不完全是您想知道的问题时,您应该提出一个新问题,该问题明确且针对您的问题。我将回滚您的编辑,以便您的问题以其原始形式陈述,并且 Ben 的回答是响应式的。请发布一个新问题。

标签: r function


【解决方案1】:

您的函数get_est 使用了一些“昂贵”的函数,例如lmvcovHC 等。如果你想到 OLS 方程,

$$ \hat{\beta} = (X^TX)^{-1}X^Ty, $$

然后你可以看到第一部分 $(X^TX)^{-1}X^T$ 在你的模拟中没有改变,所以设计矩阵是常数。为了利用这一点,我在开始模拟之前计算了 $(X^TX)^{-1}X^T$。这种方法还需要使用公式手动计算 HC3 标准误差(参见例如here

$$ \widehat{\text{Cov}}_{\text{HC3}}(\hat{\beta}) = (X^TX)^{-1}X^T \text{diag} \left[ \frac{ e_i^2}{(1-h_{ii})^2} \right] X(X^TX)^{-1}。 $$

在您的模拟迭代中,除了残差之外的所有内容都是恒定的,因此可以预先计算。一旦我实施了这些技巧,我的速度大约提高了 50 倍。

(注意:lm 使用 QR 分解,在这里也可以类似地实现。也许您可以通过并行化代码来提高速度。)

nrows = 10000
ncols = 40
n1 = 20
n2 = 20
df = data.frame(t(replicate(nrows, rnorm(ncols, 100, 3))))
cov = data.frame(group=as.factor(rep(c(1,2),c(n1,n2))))

# old function
get_est_old = function(x){
  mod = lm(x~cov$group)
  vcov = sandwich::vcovHC(mod)
  coef = as.numeric(mod$coefficients[2])
  se = sqrt(as.numeric(diag(vcov)[2]))
  stats = coef/se
  return(stats)
}

# new function
# first construct design matrix
X = matrix(c(rep(1, ncols), rep(0, ncols / 2), rep(1, ncols / 2)), ncol = 2)
# these quantities will be used below
inv = solve(crossprod(X)) %*% t(X)
h = diag(X %*% inv)

get_est_new= function(x){
  coef = (inv %*% x)
  resid = x - (X %*% coef)
  bread = (resid^2 / (1 - h)^2)[,1]
  hc3 = inv %*% diag(bread) %*% t(inv)
  se = sqrt(hc3[2,2])
  stats = coef[2,1]/sqrt(hc3[2,2])
}

# Applying above function to full data #

system.time({
  estimates_old = apply(df, 1, function(x) get_est_old(x))  
})
#>    user  system elapsed 
#>   7.876   0.042   7.929

system.time({
estimates_new = apply(df, 1, function(x) get_est_new(x))
})
#>    user  system elapsed 
#>   0.141   0.016   0.158

# check
all.equal(estimates_old, estimates_new)
#> [1] TRUE

reprex package (v2.0.1) 于 2021-09-04 创建

这些帖子可能很有趣:

【讨论】:

  • 谢谢!这个技巧有帮助!!快速的事情.. 如果我用 MASS 库中的“rlm(稳健线性模型)”替换“lm”函数,您能否建议我需要对上面的代码进行哪些其他更改,因为它基于 lm?
  • 从文档中,听起来 rlm 是基于迭代算法的,因此解决方案的预计算部分对此不起作用。
  • 谢谢@Ben!我已经编辑了我的问题,以便任何回答的人现在都知道我正在使用 rlm 而不是 lm。如果您想出办法 rlm 请在此处更新。
  • 我怀疑如果你之后改变问题,很多人会愿意回答。
  • 嗨@Ben,我已经回滚了 OP 的编辑,以便您的(非常好!)答案保持响应,并向 OP 留下评论解释原因。我还编辑了您的答案以删除顶部的免责声明。
猜你喜欢
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
  • 2023-01-15
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多