在R中没有for循环的情况下,如何在非常大的数据帧中计算累积的非零变量?

在R中没有for循环的情况下,如何在非常大的数据帧中计算累积的非零变量?,r,R,我有一个巨大的df,试图执行下面的代码需要太长时间。无论如何,要加快速度吗 df$col2 <- 0 for (i in 2:nrow(df)) { if (df$col1 > 0) { df$col2[i] <- df$col2[i-1] + 1 } else { df$col2[i] <- 0 } } 我试图使用col2对col1中的累积非零变量进行计数,并在col1中点击0时重置计数。我将注释我认为是

我有一个巨大的df,试图执行下面的代码需要太长时间。无论如何,要加快速度吗

df$col2 <- 0
for (i in 2:nrow(df)) {
    if (df$col1 > 0) {
        df$col2[i] <- df$col2[i-1] + 1
    }
    else {
        df$col2[i] <- 0
    }
}

我试图使用
col2
col1
中的累积非零变量进行计数,并在
col1
中点击
0
时重置计数。我将注释我认为是错误的内容,并显示我认为是更快的等效内容:

df$col2 <- 0
for (i in 2:nrow(df)) { 
    if (df$col1 > 0) {   # at least a "semantic" error
        df$col2[i] <- df$col2[i-1] + 1}
    else { df$col2[i] <- 0 }
}
这将构造您要求的运行计数,然后将没有增量的条目归零。

中有一个快速(但棘手)的解决方案

因此,在你的情况下:

df <- data.frame(col1 = c(1, 0, 10, 28, 0, 0, 2))

f7 <- function(x) { tmp <- cumsum(x) ; tmp - cummax((!x)*tmp) }

df$col2 <- f7(df$col1 > 0)

df
#   col1 col2
# 1    1    1
# 2    0    0
# 3   10    1
# 4   28    2
# 5    0    0
# 6    0    0
# 7    2    1

df这是一个使用
数据中的
rleid()函数的解决方案。表
包:

library(data.table)
setDT(df)[, .(col1, col2 = cumsum(col1 != 0)), by = rleid(col1 != 0)][, rleid := NULL][]
#   col1 col2
#1:    1    1
#2:    0    0
#3:   10    1
#4:   28    2
#5:    0    0
#6:    0    0
#7:    2    1
rleid()
函数是一个方便的函数,用于生成用于分组操作的运行长度类型id列(
?rleid
)。它应用于由
col1!=0
用于区分零值和非零值。在每个组中,
cumsum()
用于计算非零值。最后,从结果中删除
rleid

作为替代方案,
cumsum()

setDT(df)[, .(col1, col2 = seq_len(.N)), by = rleid(col1 != 0)][col1 == 0, col2 := 0][
  , rleid := NULL][]

但是,这也会计算未请求的后续零值。因此,对于
col1
为零的所有行,
col2
中的这些计数必须重置为零。

感谢您指出语义错误。我想输入的是df$col1[I]>0@42-你的第二行好像有个打字错误。(OP已经澄清了他的问题)@42-似乎当
col1
中出现零时,运行计数没有按照OP在澄清评论中的要求重置。谢谢。这很有帮助。你说你有一个巨大的数据帧。有多少行和列?
df <- data.frame(col1 = c(1, 0, 10, 28, 0, 0, 2))

f7 <- function(x) { tmp <- cumsum(x) ; tmp - cummax((!x)*tmp) }

df$col2 <- f7(df$col1 > 0)

df
#   col1 col2
# 1    1    1
# 2    0    0
# 3   10    1
# 4   28    2
# 5    0    0
# 6    0    0
# 7    2    1
library(data.table)
setDT(df)[, .(col1, col2 = cumsum(col1 != 0)), by = rleid(col1 != 0)][, rleid := NULL][]
#   col1 col2
#1:    1    1
#2:    0    0
#3:   10    1
#4:   28    2
#5:    0    0
#6:    0    0
#7:    2    1
setDT(df)[, .(col1, col2 = seq_len(.N)), by = rleid(col1 != 0)][col1 == 0, col2 := 0][
  , rleid := NULL][]