这是我作为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
这是完成此任务的代码,但我确信在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
}
}
答案 0 :(得分: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
要打破它:
> # 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
答案 1 :(得分:5)
这是使用其他熟悉功能的另一种方法:
seq_along(atest) - cummax(seq_along(atest) * !atest) + 1L
因为它全部是矢量化的,所以它明显快于@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
答案 2 :(得分:2)
这些问题往往适用于Rcpp
。借用@ flodel的代码作为基准测试的框架,
boolseq.cpp
-----------
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector boolSeq(LogicalVector x) {
int n = x.length();
IntegerVector output = no_init(n);
int counter = 1;
for (int i=0; i < n; ++i) {
if (!x[i]) {
counter = 1;
}
output[i] = counter;
++counter;
}
return output;
}
/*** R
x <- c(FALSE, sample( c(FALSE, TRUE), 1E5, TRUE ))
f0 <- function(x) sequence(tabulate(cumsum(!x)))
f1 <- function(x) {i <- seq_along(x); i - cummax(i * !x) + 1L}
library(microbenchmark)
microbenchmark(f0(x), f1(x), boolSeq(x), times=100)
stopifnot(identical(f0(x), f1(x)))
stopifnot(identical(f1(x), boolSeq(x)))
*/
sourceCpp
它给了我:
Unit: microseconds
expr min lq median uq max neval
f0(x) 18174.348 22163.383 24109.5820 29668.1150 78144.411 100
f1(x) 1498.871 1603.552 2251.3610 2392.1670 2682.078 100
boolSeq(x) 388.288 426.034 518.2875 571.4235 699.710 100
不太优雅,但非常接近你用R代码写的东西。