基于条件但在条件结束后重置的累积和
Cumulative sum based on a condition but reset after condition ends
如何提高按条件重置累计和的性能?
我有一个 data.frame
,在变量名称 demo
下方附加了一个演示数据集。我有一个 flag
列表示系统失败,然后是 continuousfailure
列,它计算故障连续发生的次数并在故障恢复后重置。我正在使用 tidyverse 包和 base R.
我一直在阅读一些关于这个问题的 Whosebug 帖子,但我似乎无法想出一个使用 tidyverse and/or base R 更快的解决方案。我已经实现了一个版本这使用 for 循环,但计算时间对于较大的数据集来说太长了(107388 行数据帧需要 9 分钟)。这个问题有更高效的解决方案吗?
测试数据集:
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000), flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23), rep(FALSE, 13), rep(TRUE, 5), rep(FALSE, 19)),
continuousfailure = c(rep(0, 5), 1:10, rep(0, 25), 1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)
我目前使用的代码是:
for(i in 1:length(demo$data)) {
if(demo$flag[i]) {
bin <- 0
for(j in 1:demo$continuousfailure[i]) {
bin <- bin + demo$data[i - j + 1]
}
demo$magnitude[i] <- bin
}
}
预期的输出应该是相同的,但是如果使用 tidyverse 或 base R 可以提高函数的速度,我们将不胜感激,同时还要解释一下逻辑是如何构建的。
谢谢!
我们可以使用data.table
rleid
创建群组,returncumsum
或NA
基于flag
library(dplyr)
demo %>%
group_by(group = data.table::rleid(flag)) %>%
mutate(new_mag = if(first(flag)) cumsum(data) else NA) %>%
ungroup %>%
select(-group)
# data flag continuousfailure magnitude new_mag
# <dbl> <lgl> <dbl> <dbl> <dbl>
# 1 -1121. FALSE 0 NA NA
# 2 -460. FALSE 0 NA NA
# 3 3117. FALSE 0 NA NA
# 4 141. FALSE 0 NA NA
# 5 259. FALSE 0 NA NA
# 6 3430. TRUE 1 3430. 3430.
# 7 922. TRUE 2 4352. 4352.
# 8 -2530. TRUE 3 1822. 1822.
# 9 -1374. TRUE 4 448. 448.
#10 -891. TRUE 5 -443. -443.
# … with 90 more rows
其中 magnitude
是具有来自 for
循环的值的列,new_mag
是上述代码的输出。
创建群组的方法有多种。一种是如上所示使用 rleid
,另一种是使用 dplyr
和 cumsum
中的 lag
group_by(group = cumsum(flag != lag(flag, default = first(flag)))) %>%
还有一个 base
rle
group_by(group = with(rle(flag), rep(seq_along(lengths), lengths)))
您可以将 group_by
行替换为上述任何内容。
数据
set.seed(123)
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000),
flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23),rep(FALSE, 13),
rep(TRUE, 5), rep(FALSE, 19)),continuousfailure = c(rep(0, 5), 1:10, rep(0, 25),
1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)
我们可以使用data.table
方法
library(data.table)
setDT(demo)[, new := if(first(flag)) cumsum(data) else NA_real_, rleid(flag)]
数据
set.seed(123)
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000),
flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23),rep(FALSE, 13),
rep(TRUE, 5), rep(FALSE, 19)),continuousfailure = c(rep(0, 5), 1:10, rep(0, 25),
1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)
如何提高按条件重置累计和的性能?
我有一个 data.frame
,在变量名称 demo
下方附加了一个演示数据集。我有一个 flag
列表示系统失败,然后是 continuousfailure
列,它计算故障连续发生的次数并在故障恢复后重置。我正在使用 tidyverse 包和 base R.
我一直在阅读一些关于这个问题的 Whosebug 帖子,但我似乎无法想出一个使用 tidyverse and/or base R 更快的解决方案。我已经实现了一个版本这使用 for 循环,但计算时间对于较大的数据集来说太长了(107388 行数据帧需要 9 分钟)。这个问题有更高效的解决方案吗?
测试数据集:
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000), flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23), rep(FALSE, 13), rep(TRUE, 5), rep(FALSE, 19)),
continuousfailure = c(rep(0, 5), 1:10, rep(0, 25), 1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)
我目前使用的代码是:
for(i in 1:length(demo$data)) {
if(demo$flag[i]) {
bin <- 0
for(j in 1:demo$continuousfailure[i]) {
bin <- bin + demo$data[i - j + 1]
}
demo$magnitude[i] <- bin
}
}
预期的输出应该是相同的,但是如果使用 tidyverse 或 base R 可以提高函数的速度,我们将不胜感激,同时还要解释一下逻辑是如何构建的。
谢谢!
我们可以使用data.table
rleid
创建群组,returncumsum
或NA
基于flag
library(dplyr)
demo %>%
group_by(group = data.table::rleid(flag)) %>%
mutate(new_mag = if(first(flag)) cumsum(data) else NA) %>%
ungroup %>%
select(-group)
# data flag continuousfailure magnitude new_mag
# <dbl> <lgl> <dbl> <dbl> <dbl>
# 1 -1121. FALSE 0 NA NA
# 2 -460. FALSE 0 NA NA
# 3 3117. FALSE 0 NA NA
# 4 141. FALSE 0 NA NA
# 5 259. FALSE 0 NA NA
# 6 3430. TRUE 1 3430. 3430.
# 7 922. TRUE 2 4352. 4352.
# 8 -2530. TRUE 3 1822. 1822.
# 9 -1374. TRUE 4 448. 448.
#10 -891. TRUE 5 -443. -443.
# … with 90 more rows
其中 magnitude
是具有来自 for
循环的值的列,new_mag
是上述代码的输出。
创建群组的方法有多种。一种是如上所示使用 rleid
,另一种是使用 dplyr
和 cumsum
lag
group_by(group = cumsum(flag != lag(flag, default = first(flag)))) %>%
还有一个 base
rle
group_by(group = with(rle(flag), rep(seq_along(lengths), lengths)))
您可以将 group_by
行替换为上述任何内容。
数据
set.seed(123)
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000),
flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23),rep(FALSE, 13),
rep(TRUE, 5), rep(FALSE, 19)),continuousfailure = c(rep(0, 5), 1:10, rep(0, 25),
1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)
我们可以使用data.table
方法
library(data.table)
setDT(demo)[, new := if(first(flag)) cumsum(data) else NA_real_, rleid(flag)]
数据
set.seed(123)
demo <- data.frame(data = rnorm(100, mean = 0, sd = 2000),
flag = c(rep(FALSE, 5), rep(TRUE, 10), rep(FALSE, 25), rep(TRUE, 23),rep(FALSE, 13),
rep(TRUE, 5), rep(FALSE, 19)),continuousfailure = c(rep(0, 5), 1:10, rep(0, 25),
1:23, rep(0, 13), 1:5, rep(0, 19)),magnitude = NA)