【问题标题】:Vectorized IF statement in R?R中的向量化IF语句?
【发布时间】:2010-10-28 11:27:40
【问题描述】:
x <- seq(0.1,10,0.1)
y <- if (x < 5) 1 else 2

我希望if 对每个案例进行操作,而不是对整个向量进行操作。 我需要改变什么?

【问题讨论】:

  • if (condition){} else (condition){} 构造是否也可以做到这一点?如果是否论点变得有点棘手,有时很难阅读。我有像 Christian 一样的问题,使用 if else 就像这里建议的那样工作得很好,但看起来很难看。到目前为止,我使用的表达式({yes})作为一种解决方法很好,但我仍然想知道是否有一个可以用 if 和 else 来做。

标签: r if-statement vectorization


【解决方案1】:
x <- seq(0.1,10,0.1)

> x
  [1]  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1.0  1.1  1.2  1.3  1.4  1.5
 [16]  1.6  1.7  1.8  1.9  2.0  2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9  3.0
 [31]  3.1  3.2  3.3  3.4  3.5  3.6  3.7  3.8  3.9  4.0  4.1  4.2  4.3  4.4  4.5
 [46]  4.6  4.7  4.8  4.9  5.0  5.1  5.2  5.3  5.4  5.5  5.6  5.7  5.8  5.9  6.0
 [61]  6.1  6.2  6.3  6.4  6.5  6.6  6.7  6.8  6.9  7.0  7.1  7.2  7.3  7.4  7.5
 [76]  7.6  7.7  7.8  7.9  8.0  8.1  8.2  8.3  8.4  8.5  8.6  8.7  8.8  8.9  9.0
 [91]  9.1  9.2  9.3  9.4  9.5  9.6  9.7  9.8  9.9 10.0

> ifelse(x < 5, 1, 2)
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [38] 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [75] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

【讨论】:

  • 这对于仅替换某些值也非常有用:例如x = ...; x[x &lt; 5] = 1ifelse(x &lt; 5, 1, x)
  • 如果我想返回另一个向量的元素怎么办?
  • @skan 试试y[x &lt; 5]。假设xy 的长度相同
【解决方案2】:

为了完整性:在大向量中,您可以使用索引来加快速度(我们经常在模拟中这样做,其中函数通常运行 1000 到 10000 次)。但只要没有必要,就使用ifelse。这读起来容易多了。

> set.seed(100)
> x <- runif(1000,1,10)

> system.time(replicate(10000,{
+     y <- ifelse(x < 5,1,2)
+ }))
   user  system elapsed 
   2.56    0.08    2.64 

> system.time(replicate(10000,{
+   y <- rep(2,length(x))
+   y[x < 5]<- 1
+ }))
   user  system elapsed 
   0.48    0.00    0.48 

【讨论】:

  • 您可以进一步缩短该时间。我的机器在 0.436 中执行了第二种方法(尽管第一种方法较慢),但这又提高了 200%:system.time(replicate(10000,{ y
  • @Dwin:非常好的解决方案!谢谢。但在我的机器上,它的运行速度只有一点点(0.47 与 0.48 相比)
  • 小心 - 如果x 包含NA 元素(第一个元素仍为NA,第二个元素为1),则您的两个示例不等价。
  • @jbaums 没错。添加额外的一行 y[is.na(x)]
【解决方案3】:

y &lt;- if (x &lt; 5) 1 else 2 不对整个向量进行操作(您收到的警告告诉您仅使用条件的第一个元素)。你要ifelse:

y <- ifelse(x < 5, 1, 2)

ifelse 对整个逻辑向量逐个元素地进行操作。 if 只接受 one 逻辑值。见?"if"?ifelse

【讨论】:

    【解决方案4】:

    你也可以只创建一个逻辑向量和 1 给它

    x <- seq(0.1, 10, 0.1) # Your data set   
    (x >= 5) + 1
    #  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
    # [92] 2 2 2 2 2 2 2 2 2
    

    如果要比较性能,这将是最快的解决方案

    set.seed(100)
    x <- runif(1e6, 1, 10)
    
    RL <- function(x) y <- ifelse(x < 5,1,2)
    JM <- function(x) {y <- rep(2, length(x)); y[x < 5] <- 1}
    DA <- function(x) y <- (x >= 5) + 1
    
    library(microbenchmark)
    microbenchmark(RL(x),
                   JM(x),
                   DA(x))
    
    # Unit: milliseconds
    #  expr       min        lq      mean    median        uq       max neval
    # RL(x) 331.83448 366.52940 378.89182 374.99741 381.08659 609.21218   100
    # JM(x)  38.72894  42.18745  44.36493  43.25086  44.09626  82.76168   100
    # DA(x)  10.01644  11.96482  14.21593  13.17825  14.12930  53.76923   100
    

    【讨论】:

    • 对于一个稍微更有效的方法,您可以将代码修改为function(x) y &lt;- (x &gt;= 5L) + 1L,但通常是一个不错的答案并且有趣的是,相比之下,ifelse 有多慢。
    【解决方案5】:

    按照上面的帖子,您甚至可以使用和修改满足条件的向量的元素。在我看来,如果更快地计算成本不是更高,那么应该总是这样做。

    x = seq(0.1,10,0.1)
    y <- rep(2,length(x))
    y[x<5] <- x[x<5]*2
    

    上一篇的代码最好回答这个问题。但如果我必须使用上面的代码,我会这样做:

    x = seq(0.1,10,0.1)
    y <- rep(2,length(x))
    y[x<5] <- x[x<5]*0 +1
    

    【讨论】:

    • 答案可能会根据投票情况上下移动,因此与“上述帖子”相关的问题可能会很麻烦。
    【解决方案6】:
    nzMean <- function(x) { mean(x[x!=-1],na.rm=TRUE)}
    
    nzMin <- function(x) {min(x[x!=-1],na.rm=TRUE)}
    
    nzMax <- function(x) { max(x[x!=-1],na.rm=TRUE)}
    
    nzRange<-function(x) {nzMax(x)-nzMin(x)}
    
    nzSD <- function(x) { SD(x[x!=-1],na.rm=TRUE)}
    
    #following function works
    nzN1<- function(x) {ifelse(x!=-1,(x-nzMin(x))/nzRange(x) ,x) }
    
    #following is bad as it returns only 4 not 5 elements of vector
    nzN2<- function(x) {ifelse(x!=-1,(x[x!=-1]-nzMin(x))/nzRange(x) ,x) }
    
    #following is bad as it returns 5 elements of vector but not correct answer
    nzN3<- function(x) {ifelse(x!=-1,(x[x!=-1]-nzMin(x))/nzRange(x) ,-1) }
    
    y<-c(1,-1,-20,2,4)
    a<-nzMean(y)
    b<-nzMin(y)
    c<-nzMax(y)
    d<-nzRange(y)
    # test the working function
    z<-nzN1(y)
    
    print(z)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-12
      • 2017-10-15
      • 1970-01-01
      • 2020-04-10
      • 1970-01-01
      • 2016-10-13
      相关资源
      最近更新 更多