我想在数据框中找到以前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
之前的行?还是另一种方法呢?
答案 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')
作为替代方案,您可以将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
@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解决方案比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
解决方案。