2016-06-12 3 views
6

Я хочу найти сумму из предыдущих строк n в dataframe. Например:Найти сумму предыдущих n строк в dataframe

id = 1:10 
vals = c(4,7,2,9,7,0,4,6,1,8) 
test = data.frame(id,vals) 

Так, n=3, я хочу, чтобы вычислить следующий столбец как:

test$sum = c(NA, NA, 13,18,18,16,11,10,11,15) 

Ближайший я пришел создает новый столбец с помощью:

test$valprevious = c(NA, head(test$vals,-1) 

Затем, используя цикл для повторения этого n раз, затем sum через столбцы. Я уверен, что это не самый эффективный метод, есть ли какие-либо функции, которые обращаются к n предыдущим строкам? Или другой способ сделать это?

ответ

11

Вы можете использовать функцию rollsumr из zoo пакета для этого:

library(zoo) 
test$sums <- rollsumr(test$vals, k = 3, fill = NA) 

, который дает:

> test 
    id vals sums 
1 1 4 NA 
2 2 7 NA 
3 3 2 13 
4 4 9 18 
5 5 7 18 
6 6 0 16 
7 7 4 11 
8 8 6 10 
9 9 1 11 
10 10 8 15 

Это то же самое, как с помощью функции rollsum с параметром align = 'right':

rollsum(test$vals, k = 3, fill = NA, align = 'right') 

В качестве альтернативы, вы можете использовать Reduce с shift из data.table пакета:

library(data.table) 
setDT(test)[, sums := Reduce(`+`, shift(vals, 0:2))] 

, который дает тот же результат:

> test 
    id vals sums 
1: 1 4 NA 
2: 2 7 NA 
3: 3 2 13 
4: 4 9 18 
5: 5 7 18 
6: 6 0 16 
7: 7 4 11 
8: 8 6 10 
9: 9 1 11 
10: 10 8 15 

Хорошая база R альтернатива, предложенный @ alexis_laz в комментариях:

n <- 3 
cs <- cumsum(test$vals) 
test$sums <- c(rep_len(NA, n - 1), tail(cs, -(n - 1)) - c(0, head(cs, -n))) 

Еще два вариант, предложенный @Khashaa в комментариях:

# with base R 
n <- 3 
test$sums <- c(rep_len(NA, n - 1), rowSums(embed(test$vals, n))) 

# with RcppRoll 
library(RcppRoll) 
test$sums <- roll_sumr(test$vals, 3) 

Ориентиры:

Как @alexis_laz отмечено в комментариях, некоторые из решений может создавать накладные расходы в перерасчете суммы и воссоздание length -vectors. Это может привести к различию в скорости вычислений.

# creating function of the different solutions: 
alexis_laz <- function(test) {n <- 3; cs <- cumsum(test$vals); test$sums <- c(rep_len(NA, n - 1), tail(cs, -(n - 1)) - c(0, head(cs, -n)))} 
khashaa <- function(test) {n <- 3; test$sums <- c(rep_len(NA, n - 1), rowSums(embed(test$vals, n)))} 
rcpp_roll <- function(test) test$sums <- roll_sumr(test$vals, 3) 
zoo_roll <- function(test) test$sums <- rollsumr(test$vals, k=3, fill=NA) 
dt_reduce <- function(test) setDT(test)[, sums := Reduce(`+`, shift(vals, 0:2))] 

Запуск тест на маленьком примере набора данных:

library(microbenchmark) 
microbenchmark(alexis_laz(test), 
       khashaa(test), 
       rcpp_roll(test), 
       zoo_roll(test), 
       dt_reduce(test), 
       times = 10) 

, который дает:

Unit: microseconds 
      expr  min  lq  mean median  uq  max neval cld 
alexis_laz(test) 61.390 99.507 107.7025 108.7515 122.849 131.376 10 a 
    khashaa(test) 35.758 92.596 94.1640 100.4875 103.264 112.779 10 a 
    rcpp_roll(test) 26.727 99.709 96.1154 106.1295 114.483 116.553 10 a 
    zoo_roll(test) 304.586 389.991 390.7553 398.8380 406.352 419.544 10 c 
    dt_reduce(test) 254.837 258.979 277.4706 264.0625 269.711 389.606 10 b 

Как вы можете видеть RcppRoll решение и два решения базового R от @Alexis_laz и @ Хаша значительно быстрее решений zoo и data.table (но все же в микросекундах, поэтому не о чем беспокоиться).

С гораздо большим набором данных:

test <- data.frame(id=rep(1:10,1e7), vals=sample(c(4,7,2,9,7,0,4,6,1,8),1e7,TRUE)) 

картина меняется:

Unit: milliseconds 
      expr  min   lq  mean median  uq  max neval cld 
alexis_laz(test) 3181.4270 3447.1210 4392.166 4801.410 4889.001 5002.363 10 b 
    khashaa(test) 6313.4829 7305.3334 7478.831 7680.176 7723.830 7859.335 10 c 
    rcpp_roll(test) 373.0379 380.9457 1286.687 1258.165 2062.388 2417.733 10 a 
    zoo_roll(test) 38731.0369 39457.2607 40566.126 40940.586 41114.990 42207.149 10 d 
    dt_reduce(test) 1887.9322 1916.8769 2128.567 2043.301 2218.635 2698.438 10 a 

Теперь RcppRoll решение явно быстрее с последующим data.table раствором.

+1

Альтернатива, чтобы избежать пересчета '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' cs = cumsum (test $ vals); c (rep_len (NA, n - 1), tail (cs, - (n - 1)) - c (0, head (cs, -n))) –

+0

@alexis_laz Thx! Это очень хорошая альтернатива R. Добавил его к ответу. – Jaap

+1

'rowSums (embed (test $ vals, 3))' использовались как наиболее эффективные в дни pre-'RcppRoll'. – Khashaa

Смежные вопросы