运行长度的累计和。这个循环可以被矢量化吗?

时间:2011-11-17 17:02:06

标签: r vectorization plyr data.table

我有一个数据框,我在其上计算特定列的运行长度编码。列dir的值为-1,0或1。

dir.rle <- rle(df$dir)

然后我获取运行长度并计算数据帧中另一列的分段累积和。我正在使用for循环,但我觉得应该有一种更聪明地做到这一点的方法。

ndx <- 1
for(i in 1:length(dir.rle$lengths)) {
    l <- dir.rle$lengths[i] - 1
    s <- ndx
    e <- ndx+l
    tmp[s:e,]$cumval <- cumsum(df[s:e,]$val)
    ndx <- e + 1
}

dir的游程长度为每次运行定义了开始,s和结束,e。上面的代码有效,但它不像惯用的R代码。我觉得好像应该有另一种方法来做这个没有循环。

3 个答案:

答案 0 :(得分:12)

这可以分解为两步问题。首先,如果我们根据rle创建索引列,那么我们可以使用它来分组并运行cumsum。然后可以通过任何数量的聚合技术来执行group by。我将展示两个选项,一个使用data.table,另一个使用plyr

library(data.table)
library(plyr)
#data.table is the same thing as a data.frame for most purposes
#Fake data
dat <- data.table(dir = sample(-1:1, 20, TRUE), value = rnorm(20))
dir.rle <- rle(dat$dir)
#Compute an indexing column to group by
dat <- transform(dat, indexer = rep(1:length(dir.rle$lengths), dir.rle$lengths))


#What does the indexer column look like?
> head(dat)
     dir      value indexer
[1,]   1  0.5045807       1
[2,]   0  0.2660617       2
[3,]   1  1.0369641       3
[4,]   1 -0.4514342       3
[5,]  -1 -0.3968631       4
[6,]  -1 -2.1517093       4


#data.table approach
dat[, cumsum(value), by = indexer]

#plyr approach
ddply(dat, "indexer", summarize, V1 = cumsum(value))

答案 1 :(得分:4)

Spacedman&amp; Chase提出了一个关键点,即分组变量简化了所有事情(Chase提出了两种很好的方法从那里开始)。

我将提出另一种方法来形成该分组变量。它不使用rle,至少对我来说,感觉更直观。基本上,在diff()检测到值更改的每个点,将形成您的分组变量的cumsum增加1:

df$group <- c(0, cumsum(!(diff(df$dir)==0)))

# Or, equivalently
df$group <- c(0, cumsum(as.logical(diff(df$dir))))

答案 2 :(得分:2)

将“组”列添加到数据框。类似的东西:

df=data.frame(z=rnorm(100)) # dummy data
df$dir = sign(df$z) # dummy +/- 1
rl = rle(df$dir)
df$group = rep(1:length(rl$lengths),times=rl$lengths)

然后使用tapply在组内求和:

tapply(df$z,df$group,sum)