【问题标题】:Faster if statement evaluations in RR中更快的if语句评估
【发布时间】:2014-01-21 22:59:53
【问题描述】:

所以我想弄清楚是否有更好的方法可以将多个条件语句添加到R 的 if 子句中,以加快处理速度。下面是我编写的一些代码,它们在简单的情况下在大型数据集上运行得非常快,而在不太简单的情况下运行得不是那么快。任何建议都非常感谢!此外,tic-toc 函数位于问题的最底部,以防您想自己运行它并查看该函数的运行速度。

此外,为了直观地了解代码在做什么,第一块只是简单地确定是否有任何一对 x 和 y 的值大于其他所有 x 和 y。

第二段代码做同样的事情,但是,它添加了任何 x 值实际上彼此相等的条件,然后检查哪个具有最低的 y 值。同样,如果任何一个 y 值彼此相等,则检查哪个具有最低的 x 值。

所以,在简单的情况下运行代码我有以下内容:

tic()

x = runif(10000)
y = runif(10000)

front = 1:length(x)

for(i in 1:length(x)){
    for(n in 1:length(x)){
        if((x[i]>x[n]  &  y[i]>y[n])){
            front[i] = NA
            break
        }
    }
}

toc()

如您所见,我只评估x[i]>x[n] & y[i]>y[n] 的单一条件

目录() 过去 1.28

上面的代码运行时间为 1.28 秒。 现在,当我有三个要检查的条件时运行代码,我有以下内容:

tic()

x = runif(10000)
y = runif(10000)

front = 1:length(x)

for(i in 1:length(x)){
    for(n in 1:length(x)){
        if((x[i]>x[n]  &  y[i]>y[n]) | (x[i]==x[n] & y[i]!=min(y[which(x==x[i])])) | (y[i]==y[n] & x[i]!=min(x[which(y==y[i])]))){
            front[i] = NA
            break
        }
    }
}


toc()

如您所见,我现在必须检查 if 语句中的三个条件,即,

(x[i]>x[n]  &  y[i]>y[n]) | (x[i]==x[n] & y[i]!=min(y[which(x==x[i])])) | (y[i]==y[n] & x[i]!=min(x[which(y==y[i])]))

但是,这会导致 R 中的计算负担巨大,并使代码变得更慢。

> toc()
elapsed 
  74.47

我们看到运行新修改的代码现在显着减慢到 74.47 秒。现在,我正在寻找可以加快我的代码速度的替代函数调用,或者只是以“更好”的方式重写它,以使代码不那么慢。

如果需要,这里是 tic-toc 函数的代码:

tic <- function(gcFirst = TRUE, type=c("elapsed", "user.self", "sys.self"))
{
   type <- match.arg(type)
   assign(".type", type, envir=baseenv())
   if(gcFirst) gc(FALSE)
   tic <- proc.time()[type]         
   assign(".tic", tic, envir=baseenv())
   invisible(tic)
}

toc <- function()
{
   type <- get(".type", envir=baseenv())
   toc <- proc.time()[type]
   tic <- get(".tic", envir=baseenv())
   print(toc - tic)
   invisible(toc)
}

为 sashkello 编辑

所以我的代码现在看起来像这样:

library(mvtnorm)
#Here are the variables I will be working with 

> x
 [1] 0.53137100 0.75357474 0.87904120 0.29727488 0.00000000 0.00000000
 [7] 0.00000000 0.00000000 0.00000000 0.04059217
> y
 [1]  4.873500  3.896917  1.258215  5.776484 12.475491  5.273784 13.803158
 [8]  4.472204  2.629839  6.689242
> front
 [1] NA NA  3 NA NA NA NA NA  9 NA
> all.preds
[1] 0.596905183 0.027696850 1.005666896 0.007688514 3.900000000

    x = x[!is.na(front)]
    y = y[!is.na(front)]

    mu = c(all.preds[1],all.preds[3])
    sigma = matrix(c(all.preds[2],0,0,all.preds[4]),nrow=2)

    z = rmvnorm(10000,mu,sigma)
    z[,1] = sapply(z[,1],function(x){max(x,0)})

    points(z,col="black",pch=19,cex=.01)
    temp = 1:nrow(z)

    for(i in 1:length(temp)){
        cond1 = z[i,2]!=min(z[which(z[,1]==z[i,1]),2])
        cond2 = z[i,1]!=min(z[which(z[,2]==z[i,2]),1])
        for(n in 1:length(x)){
            if((z[i,1]>x[n]  &  z[i,2]>y[n]) | (z[i,1]==x[n] & cond1) | (z[i,2]==y[n] & cond2)){
                temp[i] = NA
                break
            }
        }
    }
    prop = sum(!is.na(temp))/length(temp)

而 cond1 和 cond2 语句仍然需要很长的时间。有什么建议吗?

【问题讨论】:

  • 我认为您可以将cond1cond2 的计算排除在循环之外,并使用outer 一次计算所有cond1[i]/cond2[i]
  • @Roland,你能扩展一下这个想法,和/或提供一个示例代码的 sn-p 吗?

标签: r performance if-statement


【解决方案1】:

您可以将y[i]!=min(y[which(x==x[i])])x[i]!=min(x[which(y==y[i])]) 放在第二个循环之前,因为它们都只涉及i

for(i in 1:length(x)){
    cond1 = y[i]!=min(y[which(x==x[i])])
    cond2 = x[i]!=min(x[which(y==y[i])])
    for(n in 1:length(x)){
        if((x[i]>x[n]  &  y[i]>y[n]) | (x[i]==x[n] & cond1) | (y[i]==y[n] & cond2)){

这应该会大大加快速度,因为minwhich 都非常慢,而且您每次都在第二个循环中运行它们。

【讨论】:

  • 哦,但我只想在 x[i]==x[n] 或 y[i]==y[n] 时评估 min 和 which 语句,因此您的建议将不起作用。
  • 我们说话的时候我正在修补它。我向您展示的代码是一个更大更复杂事物的一小部分。在我重新编码一些东西后,我会及时通知你。
  • 所以在这段代码中它运行得非常快,但是,当我在其他代码中使用它时,它仍然运行(出乎意料地)非常慢。无论如何我可以向您展示我的代码的另一部分,看看您是否有任何建议?它基本上是完全相同的代码,只是具有更多“定义”的数据值。我将在上面添加一个编辑部分。
  • 是的,只需将其复制粘贴到问题中即可。我不知道我能帮上多少忙,但仔细观察它不会有什么坏处……
  • 复制粘贴在那里,以防你没有看到它。
【解决方案2】:

既然您提出了要求,这里有一种在 for 循环之外计算 cond1 的有效方法(您可能根本不需要):

#some data_
set.seed(42)
z <- matrix(sample(1:5, 200, TRUE), ncol=2)

#your loop
cond1 <- logical(100)

for (i in 1:100) {
cond1[i] = z[i,2]!=min(z[which(z[,1]==z[i,1]),2])
}

#alternative
library(data.table)
DT <- data.table(z)
DT[, id:=.I]

DT[, cond1:=V2!=min(V2), by=V1]

#compare results
identical(DT[, cond1], cond1)
#[1] TRUE

【讨论】:

  • 有没有办法按照你之前的建议使用外部命令?
  • 可能有。但是它仍然会做很多不必要的计算并且需要太多的内存。
  • 我确信您的 data.table 方法有效,但可以解释原因吗?我阅读了帮助文件,但仍然很难理解它为什么起作用。
  • 这很容易。对于第 1 列的每个唯一值(自动命名为 V1,但您可以自己命名这些列),它计算第 2 列 (V2) 中相应值的最小值,并将这些值中的每一个与该最小值进行比较。比较的结果分配给一个新列cond1。所有这些都非常有效,因为 (a) 它是矢量化的,并且 (b) 避免了将对象复制到内存中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多