【问题标题】:R: 'vectorizing' a triple loopR:“矢量化”三重循环
【发布时间】:2014-11-29 17:07:13
【问题描述】:

我在 R 中编写了一段代码,用于计算所谓的排名统计的双倍总和。

我需要重复 Q 最小 1000 次的计算,但是里面有 3 个循环,只做一次就需要很长时间。

这是我的代码:

#u, a - real numbers
l <- function(u, a) {
  -sqrt((1-a)/a)*I(u>=0 & u<a) + sqrt(a/(1-a))*I(u>=a & u<=1)
}


# r,s - real number, R,S - vectors of real numbers (equal lengths)
L<-function(r, s, R, S) {
  n<-length(R)
  x<-0
  for (i in 1:n) {
    x<-x+l(R[i]/(n+1),r) * l(S[i]/(n+1),s)
  }
  1/sqrt(n)*x
}

# r, s, X, Y - vectors of real numbers; X and Y must be equally long
Q<-function(r,s,X,Y) {
  n<-length(X)
  R<-rank(X)
  S<-rank(Y)
  q<-0
  for (j in 1:length(r)) {
    for (k in 1:length(s)) {
      q<-q+L(r[j],s[k],R,S)^2

    }
  } 
  q
}

我尝试使用 sapply 和 apply 转换我的函数,但是第一个函数失败了,因为 r 和 s 的大小可能不相等(r、s 的长度也不应该等于 X(或 Y ))。

有什么方法可以生成一个函数 L,它接受 4 个向量并生成一个矩阵,这样我就可以摆脱循环了吗?

提前致谢!

//编辑:

我已经使用 mapply 编写了一个替代函数:

Q1<-function(r,s,X,Y) {
  n<-length(X)
  R<-rank(X)
  S<-rank(Y)
  rs <- expand.grid(r,s)
  q<-do.call(mapply, c(function(r,s) L(r,s,R=R,S=S)^2, unname(rs)))
  sum(q)
}

但它似乎更慢。

【问题讨论】:

  • 使用 L(r[j],s[k],R,S)^2 可能比调用 L 函数两次来做:L(r[j],s[k],R,S)*L(r[j],s[k],R,S) 更快。预维向量并分配到位置也比连接更快。
  • 嗯,确实 - 使用 ^2 而不是乘法有帮助,它现在快了近 2 倍,非常感谢。我还在这两个函数中用累积求和替换了串联,它节省了大约。 0.1 秒。不过,仍然想知道是否有办法省略循环(前提是确实需要更少的时间)。
  • 不确定您是否理解了预先维度的建议。你应该编辑你的代码来显示你做了什么。
  • 我想我做到了(你的意思是声明指定长度的向量,然后用值填充它们,不是吗?)但我只是尝试了一些我认为会更快的方法(也许我错了)。我编辑了我的代码。

标签: r sum vectorization apply sapply


【解决方案1】:

如果您想为 rs 的不同值生成 L(.) 的所有值,那么无循环方法可能是:

  rs <- expand.grid(r=r,s=s); rm(r); rm(s)
  #edit
  rs$qrs <- with(rs, L(r, s, R, S)^2 )
  q <- sum(rs$qrs)

我不相信这会更快。有一个普遍但错误的概念,即 R 中的循环效率低下。效率上的大部分收益将来自于简化内部功能。

    >  set.seed(123)
>    r <- runif(4)
>    s <- runif(3)
>    rs <- expand.grid(r=r,s=s)
> rs
           r         s
1  0.2875775 0.9404673
2  0.7883051 0.9404673
3  0.4089769 0.9404673
4  0.8830174 0.9404673
5  0.2875775 0.0455565
6  0.7883051 0.0455565
7  0.4089769 0.0455565
8  0.8830174 0.0455565
9  0.2875775 0.5281055
10 0.7883051 0.5281055
11 0.4089769 0.5281055
12 0.8830174 0.5281055
> rs$qrs <- with(rs, L(r, s, 1:10, 1:10)^2 )
>   q <- sum(rs$qrs)
> q
[1] 14.39009
> rs
           r         s          qrs
1  0.2875775 0.9404673 0.0004767998
2  0.7883051 0.9404673 0.0003911883
3  0.4089769 0.9404673 6.6571168565
4  0.8830174 0.9404673 0.0017673788
5  0.2875775 0.0455565 0.0004767998
6  0.7883051 0.0455565 0.0003911883
7  0.4089769 0.0455565 6.6571168565
8  0.8830174 0.0455565 0.0017673788
9  0.2875775 0.5281055 0.0004767998
10 0.7883051 0.5281055 0.0003911883
11 0.4089769 0.5281055 6.6571168565
12 0.8830174 0.5281055 0.0017673788

【讨论】:

  • rs$qrs
  • 对。应该包括with(rs, )
  • 并取出索引。
  • 嗯,就时间而言,它工作得非常好(对于长度为 100 的 X、Y 和长度为 8,4 的 r、s 分别快约 30 倍),但我担心结果是错误的...... Q 和 Q1 函数(将在我的第一篇文章中看到)对于固定输入给出相同的结果,您提出的结果不同。亲自查看 X=Y=rnorm(100) 和 r, s - (0,1) 内的任何数字向量。你确定这应该为 (r,s) 的所有组合生成 L(.) 吗?
  • 它确实产生了 32 个数字,但它们与我从 Q 函数中得到的不同。当 L 的最后两个参数(即 R 和 S)是向量时,with 是否正常工作?
猜你喜欢
  • 1970-01-01
  • 2016-02-11
  • 2019-09-20
  • 2020-05-03
  • 1970-01-01
  • 2012-06-11
  • 2019-09-16
  • 2019-02-28
相关资源
最近更新 更多