2016-06-12 62 views
6

我想查找數據幀中以前的n行的總和。例如:查找數據幀中前n行的總和

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

所以,對於n=3,我要計算下一列:

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

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

我來創造使用新列最接近

然後使用循環遍歷列重複此n次,然後sum。我相信這不是最有效的方法,是否有任何訪問前一行的函數?或者另一種方式來做到這一點?

回答

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') 

作爲替代方案,可以使用Reduceshiftdata.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 - 的傳單。這可能會導致計算速度的差異。

# 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解決方案和@Alexis_laz兩個基礎R解決方案和@Khashaa比zoodata.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

另一種方法是避免重新計算'sum'並創建'length(vals)'向量,可能是'n = 3; cs = cumsum(test $ vals); c(0,head(cs,-n)))' –

+0

@alexis_laz Thx!這是一個非常好的基礎R選擇。將它添加到答案中。 – Jaap

+1

'rowSums(embed(test $ vals,3))'曾經是''RcppRoll'前期最有效的''。 – Khashaa