比ifelse更有效的比较数字的方法?

时间:2015-10-21 18:14:20

标签: r

考虑简单数据:

    > cbind(x,y)
           x  y
    [1,]  -1 99
    [2,]   5  4
    [3,]  10 -2
    [4,] 600  0
    [5,] -16  1
    [6,]   0 55

现在考虑这个简单的嵌套ifelse语句:

ifelse(y>=0, ifelse(x<0,y,ifelse(x>y,y,x)), x)

这给了我一个结果:

[1] 99  4 10  0  1  0

应该很容易看出代码的作用:它用x替换x中的值:

1)如果x,y都是非负的

,则y值越小

2)如果x为负,则为y的任何非负值

或单独留下x。

我的问题是:这段代码的计算效率不高,你能想出任何有效编码的方法吗?谢谢!

4 个答案:

答案 0 :(得分:3)

您可以使用x是总和,y(x+y)/2(x-y)/2的差异。然后用逻辑表达式计算(TRUE等于1,FALSE等于0):

(x+y + (1+2*((x<=y)*(x>=0)-(y>=0)))*(x-y))/2

给出与嵌套ifelse - 表达式相同的结果。 速度比较,使用长度为500的向量:

> set.seed(1)

> x <- sample(-100:100,500,replace=TRUE)

> y <- sample(-100:100,500,replace=TRUE)

> system.time(
+   for ( i in 1:100000 )
+   {
+     A <- (x+y + (1+2*((x<=y)*(x>=0)-(y>=0)))*(x-y))/2
+   }
+ )
   user  system elapsed 
   8.46    0.00    8.51 

> system.time(
+   for ( i in 1:100000 )
+   {
+     B <- ifelse(y>=0, ifelse(x<0,y,ifelse(x>y,y,x)), x)
+   }
+ )
   user  system elapsed 
  74.58    0.03   75.05 

> system.time(
+   for ( i in 1:100000 )
+   {
+     z <- y
+     z[(x < y & x >= 0)| y < 0] <- x[(x < y & x >= 0)| y < 0];z
+   }
+ )
   user  system elapsed 
  23.32    0.00   23.44 

检查结果是否相同:

> all(A==B)
[1] TRUE
> all(A==z)
[1] TRUE
> 

答案 1 :(得分:3)

没有索引的另一个选项:

x * ((x < y & x >= 0) | y < 0) + y * ((x > y & y >= 0) | x < 0) 

输出:

[1] 99  4 10  0  1  0

时间比较,似乎mra68答案是最快的:

library(microbenchmark)
microbenchmark(
  TylerRinker = z[(x < y & x >= 0)| y < 0] <- x[(x < y & x >= 0)| y < 0],
  mra68 =(x+y + (1+2*((x<=y)*(x>=0)-(y>=0)))*(x-y))/2,
  mpalanco = x *((x < y & x >= 0)| y < 0)+ y * ((x > y & y >= 0)| x < 0),
  if_else = ifelse(y>=0, ifelse(x<0,y,ifelse(x>y,y,x)), x)
  ) 

   Unit: microseconds
        expr    min      lq     mean median     uq     max neval cld
 TylerRinker  8.800  9.7780 11.47480 10.267 10.268  75.778   100  a 
       mra68  5.867  6.3560  9.40188  6.845  7.334 214.623   100  a 
    mpalanco  7.334  7.8230  8.67836  8.311  8.800  30.312   100  a 
     if_else 44.489 45.9565 54.61929 53.289 53.290 245.911   100   b

答案 2 :(得分:2)

也许只是索引。我不知道它是否更有效率:

dat <- read.table(text="       x  y
    [1,]  -1 99
    [2,]   5  4
    [3,]  10 -2
    [4,] 600  0
    [5,] -16  1
    [6,]   0 55", header=TRUE)



x <- dat[, 1]
y <- dat[, 2]

z <- y
z[(x < y & x >= 0)| y < 0] <- x[(x < y & x >= 0)| y < 0];z

## 99  4 10  0  1  0

答案 3 :(得分:1)

这是对上述答案的总结,而不是一个独特的答案;但我确实提供了时间比较。

通过组合操作,

b可以加快速度。 c-e以前都是提供的答案。 @ mra68的答案显得最快

library(microbenchmark)
microbenchmark(a= ifelse(y>=0, ifelse(x<0,y,ifelse(x>y,y,x)), x),
               b= {ifelse(y>= 0, ifelse(x>y | x<0, y,x), x)},
               c= {z <- y; z[(x < y & x >= 0)| y < 0] <- x[(x < y & x >= 0)| y < 0];z},
               d= x * ((x < y & x >= 0) | y < 0) + y * ((x > y & y >= 0) | x < 0),
               e= (x+y + (1+2*((x<=y)*(x>=0)-(y>=0)))*(x-y))/2)

Unit: microseconds
 expr    min      lq     mean median     uq    max neval cld
    a 16.346 18.6270 21.88066 19.387 20.528 77.548   100   c
    b 10.644 11.4040 13.05781 11.785 12.545 39.154   100  b 
    c  3.801  4.1820  5.10146  4.562  4.942 18.247   100 a  
    d  3.041  3.4210  4.37168  3.801  3.802 33.452   100 a  
    e  2.281  2.8515  3.36810  3.041  3.421 18.246   100 a  

虽然,IMO,在最快的解决方案中缺乏可读性并不值得加速。

根据实际使用情况,您可以通过订购if-else操作来实现加速,以使最小数量的操作在调用堆栈中向下传递。