Кумулятивные суммы по длинам пробега.Может ли этот цикл быть векторизованным? - PullRequest
4 голосов
/ 17 ноября 2011

У меня есть фрейм данных, для которого я вычисляю кодировку длины прогона для определенного столбца.Значения столбца 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 ]

12 голосов
/ 17 ноября 2011

Это можно разбить на два этапа.Во-первых, если мы создадим столбец индексации на основе rle, то мы можем использовать его для группировки и запуска cumsum.Группировка может быть выполнена любым количеством методов агрегации.Я покажу два варианта, один с использованием 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))
4 голосов
/ 17 ноября 2011

И Spacedman & Chase подчеркивают, что группирующая переменная упрощает все (и Чейз излагает два хороших пути для продолжения).

Я просто добавлю альтернативный подход к формированию этой группирующей переменной. Он не использует rle и, по крайней мере для меня, чувствует себя более интуитивным. По сути, в каждой точке, где diff() обнаруживает изменение значения, cumsum, который будет формировать вашу переменную группировки, увеличивается на единицу:

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

# Or, equivalently
df$group <- c(0, cumsum(as.logical(diff(df$dir))))
2 голосов
/ 17 ноября 2011

Добавить столбец «группа» во фрейм данных.Что-то вроде:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...