更优雅的方式返回基于布尔值的数字序列?

tca*_*h21 11 r

这是我作为data.frame的一部分的布尔样本:

atest <- c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE)

我想从每个FALSE返回一个从1开始的数字序列,然后增加1直到下一个FALSE.

得到的所需矢量是:

[1]  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 10  1
Run Code Online (Sandbox Code Playgroud)

下面是实现此代码,但我敢肯定有一个更简单或更优雅的方式在R.做到这一点,我一直在努力学习如何R中更高效地编写代码的东西,而不是简单地完成工作.

result <- c()
x <- 1
for(i in 1:length(atest)){
    if(atest[i] == FALSE){
        result[i] <- 1
        x <- 1
    } 
    if(atest[i] != FALSE){
        x <- x+1
         result[i] <- x
    }
}
Run Code Online (Sandbox Code Playgroud)

Jos*_*ich 19

这是一种方法,使用方便(但不是广为人知/使用)的基本功能:

> sequence(tabulate(cumsum(!atest)))
 [1]  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 10  1
Run Code Online (Sandbox Code Playgroud)

要打破它:

> # return/repeat integer for each FALSE
> cumsum(!atest)
 [1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3
> # count the number of occurrences of each integer
> tabulate(cumsum(!atest))
[1] 10 10  1
> # create concatenated seq_len for each integer
> sequence(tabulate(cumsum(!atest)))
 [1]  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 10  1
Run Code Online (Sandbox Code Playgroud)

  • @Joshua Ulrich +1这个伟大的解决方案; 但如果第一个元素不是"FALSE"则失败:`sequence(tabulate(cumsum(!atest [-1]))) (2认同)

flo*_*del 5

以下是使用其他熟悉功能的另一种方法:

seq_along(atest) - cummax(seq_along(atest) * !atest) + 1L
Run Code Online (Sandbox Code Playgroud)

因为它全部是矢量化的,所以它明显快于@Joshua的解决方案(如果速度有任何问题):

f0 <- function(x) sequence(tabulate(cumsum(!x)))
f1 <- function(x) {i <- seq_along(x); i - cummax(i * !x) + 1L}
x  <- rep(atest, 10000)

library(microbenchmark)
microbenchmark(f0(x), f1(x))
# Unit: milliseconds
#   expr       min        lq    median        uq      max neval
#  f0(x) 19.386581 21.853194 24.511783 26.703705 57.20482   100
#  f1(x)  3.518581  3.976605  5.962534  7.763618 35.95388   100

identical(f0(x), f1(x))
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)