在数据帧中查找前n行的总和

时间:2016-06-12 10:49:30

标签: r dataframe

我想在数据框中找到以前n行的总和。 E.g:

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之前的行?还是另一种方法呢?

1 个答案:

答案 0 :(得分:15)

您可以使用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')

作为替代方案,您可以将Reduceshift包中的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

@alexis_laz在评论中提出的一个很好的基础R替代方案:

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和@Khashaa的两个基本R解决方案比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解决方案。