2016-06-12 6 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를 들어, I는 다음 열 계산 싶어 :

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

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

을 내가 사용 새 열을 만드는 왔어요 가장 가까운

그런 다음 루프를 사용하여 n 번을 반복 한 다음 열을 통해 sum을 반복합니다. 이것이 가장 효율적인 방법이 아니라고 확신합니다. n 이전 행에 액세스하는 함수가 있습니까? 또는 다른 방법으로이 작업을 수행 할 수 있습니까?

답변

11

당신은이에 대한 zoo 패키지에서 rollsumr 기능을 사용할 수 있습니다 :

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 

이것은 align = 'right' 매개 변수와 함께 rollsum 기능을 사용하는 것과 같습니다 :

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

01 동일한 결과를 제공

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

: 대안으로 23,516,

, 당신은 data.table 패키지에서 shift으로 Reduce을 사용할 수 있습니다

> 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))) 
,451,515,

코멘트에 @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- 벡터를 다시 만듭니다. 이로 인해 계산 속도가 달라질 수 있습니다.

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

제공 : 작은 예를 들어 데이터 세트에 대한 벤치 마크를 실행

# 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))] 

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

관련 문제