分别计算每个块内的连续TRUE值

时间:2018-01-31 21:10:33

标签: r

已经提出了类似的问题,我一直试图将各种答案(mobilePhonerle等)拼凑起来,但这需要花费数小时的时间,而我仍然没有到那儿。

我的数据集的列只包含cumsum / TRUE,例如:

FALSE

对于每组连续x <- c(FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE) 值,我想计算该集合中TRUE的数量。可以忽略TRUE值,即我想要输出上述数据,如下所示:

FALSE

7 个答案:

答案 0 :(得分:5)

基础R中的一个简单的:

ave(x, cumsum(!x), FUN = cumsum)

#[1] 0 0 1 2 3 0 1 0 1 2 0

答案 1 :(得分:4)

sequence(rle(x)$lengths) * x
#[1] 0 0 1 2 3 0 1 0 1 2 0

或者,如果您可以考虑非base10^6向量的速度提高约20倍)

library(data.table)
rowid(rleid(x))*x
# [1] 0 0 1 2 3 0 1 0 1 2 0

答案 2 :(得分:3)

可能有点难看,但在这里我们使用rle()来查找TRUE值的运行。然后使用seq.int()索引组(这也将使组成FALSE),但我们乘以该值,使FALSE索引变为0。

x <- c(FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE)
with(rle(x), unlist(Map(`*`, sapply(lengths, seq.int), values)))
# [1] 0 0 1 2 3 0 1 0 1 2 0

答案 3 :(得分:3)

爵士 考虑专门为运行计数,求和等创建的runner包。完全用C ++编写。

devtools::instal_github("gogonzo/runner")
library(runner)

x <- c(FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE)
streak_run(x)*x 
# [1] 0 0 1 2 3 0 1 0 1 2 0

函数streak_run计算连续出现的TRUE和FALSE,并乘以x是这种情况下ifelse的更快更简单的版本。

还可以指定k参数,该参数定义窗口大小。窗口大小可以是常量,也可以由相同长度的其他矢量指定。

答案 4 :(得分:2)

您可以使用Reduce我们添加数字,但如果下一个数字为零,我们会再次开始添加。它用ifelse调整cumsum函数。即。 Reduce(function(a,b),a+b,x,,T)cumsum(x)函数。现在我们只引入ifelse语句,以便下一个值为零时,将总和设置为零并再次开始添加。这是代码:

Reduce(function(a,b)ifelse(b==0,0,a)+b,x,accumulate = T)
 [1] 0 0 1 2 3 0 1 0 1 2 0

您也可以使用<<-并实施与上述相同的逻辑

c(b<-0,sapply(x,function(a)b<<-ifelse(a==0,b<-0,a)+b))[-1]#Remove the first b<-0 that I added
 [1] 0 0 1 2 3 0 1 0 1 2 0

在第一个中,累计金额为a,而在第二个累计金额为b

答案 5 :(得分:2)

总是有一些有趣的方法来使用cumsum来做这种反击:

x[x] <- ave(x[x], cumsum(!x)[x], FUN=seq_along)
x
# [1] 0 0 1 2 3 0 1 0 1 2 0

答案 6 :(得分:1)

以下是使用splitcumsum的另一个选项:

unlist(sapply(split(x, cumsum(x == FALSE)), cumsum), use.names = F)
# [1] 0 0 1 2 3 0 1 0 1 2 0

基准

以下是目前所有解决方案的microbenchmark结果:

library(microbenchmark);
library(runner);

set.seed(2017);
x <- sample(c(TRUE, FALSE), 10^4, replace = T);

microbenchmark(
    cumsum_thelatemail = ave(x[x], cumsum(!x)[x], FUN=seq_along),
    reduce_Onyambu = Reduce(function(a,b)ifelse(b==0,0,a)+b,x,accumulate = T),
    rle_MrFlick = with(rle(x), unlist(Map(`*`, sapply(lengths, seq.int), values))),
    runner_Gonzo = streak_run(x)*x,
    sequence_Henrik = sequence(rle(x)$lengths) * x,
    split_Evers = unlist(sapply(split(x, cumsum(x == FALSE)), cumsum), use.names = F)
)
#Unit: microseconds
#               expr       min        lq       mean    median        uq
# cumsum_thelatemail  3569.336  3713.939  4196.6491  3802.570  4115.896
#     reduce_Onyambu 40599.427 41884.466 45887.2020 43222.302 49277.158
#        rle_MrFlick  9349.131  9907.766 11353.1854 10602.481 11213.147
#       runner_Gonzo   275.912   293.085   316.6987   295.656   300.059
#    sequence_Henrik  2696.624  2840.593  3177.7400  2956.738  3179.673
#        split_Evers  4772.078  4954.352  5423.3227  5193.803  5528.410
#       max neval
#  11360.39   100
# 103999.41   100
#  46731.03   100
#    538.49   100
#  11670.56   100
#  13607.49   100