2013-10-05 15 views
9

załóżmy RI mają następujące wektor:Kolejne/walcujące sumy w wektorze w R

[1 2 3 10 20 30] 

Jak przeprowadzić operację, podczas której dla każdego indeksu 3 kolejne elementy są sumowane, w wyniku następnego wektora:

[6 15 33 60] 

gdzie pierwszy element = 1 + 2 + 3, drugi element = 2 + 3 + 10 itd ...? Dzięki

Odpowiedz

18

To, co masz, to wektor, a nie tablica. Możesz użyć funkcji rollapply z pakietu zoo, aby uzyskać to, czego potrzebujesz.

> x <- c(1, 2, 3, 10, 20, 30) 
> #library(zoo) 
> rollapply(x, 3, sum) 
[1] 6 15 33 60 

Spójrz na ?rollapply celu uzyskania szczegółowych informacji na temat tego, co rollapply ma i jak go używać.

+1

dzięki temu właśnie tego chciałem. Zaznaczę jako odpowiedź (nie mogę teraz zrobić z powodu limitu czasowego). Czy to najszybszy sposób na zrobienie tego? Dzięki – user2834313

9

Korzystanie tylko zasady R można zrobić:

v <- c(1, 2, 3, 10, 20, 30) 
grp <- 3 

res <- sapply(1:(length(v)-grp+1),function(x){sum(v[x:(x+grp-1)])}) 

> res 
[1] 6 15 33 60 

inny sposób, szybciej niż sapply (porównywalny do użytkownika @ flodel rsum.cumsum), jest następujący:

res <- rowSums(outer(1:(length(v)-grp+1),1:grp,FUN=function(i,j){v[(j - 1) + i]})) 

Oto flodel użytkownika Zaktualizowano benchmarku:

x <- sample(1:1000) 

rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum) 
rsum.sapply <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){sum(x[i:(i+n-1)])}) 
rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))] 
rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1) 
rsum.outer <- function(x, n = 3L) rowSums(outer(1:(length(x)-n+1),1:n,FUN=function(i,j){x[(j - 1) + i]})) 


library(microbenchmark) 
microbenchmark(
    rsum.rollapply(x), 
    rsum.sapply(x), 
    rsum.filter(x), 
    rsum.cumsum(x), 
    rsum.outer(x) 
) 


# Unit: microseconds 
#    expr  min  lq  median   uq  max neval 
# rsum.rollapply(x) 9464.495 9929.4480 10223.2040 10752.7960 11808.779 100 
# rsum.sapply(x) 3013.394 3251.1510 3466.9875 4031.6195 7029.333 100 
# rsum.filter(x) 161.278 178.7185 229.7575 242.2375 359.676 100 
# rsum.cumsum(x) 65.280 70.0800 88.1600 95.1995 181.758 100 
#  rsum.outer(x) 66.880 73.7600 82.8795 87.0400 131.519 100 
+0

Awesome! dzięki. Niestety nie mogę głosować, ponieważ nie mam wystarczającej liczby punktów. – user2834313

+0

@ user2834313: bez problemu;) – digEmAll

+1

Dodano nowy możliwy sposób;) – digEmAll

10

Jeśli prędkość jest problemem, można użyć filtru splot i odciąć końce:

rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))] 

lub nawet szybciej, zapisz go jako różnicę między kumulacji dwóch kwot:

rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1) 

zarówno do użytku tylko funkcje podstawowe. Niektóre punkty odniesienia:

x <- sample(1:1000) 

rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum) 
rsum.sapply <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){ 
             sum(x[i:(i+n-1)])}) 

library(microbenchmark) 
microbenchmark(
    rsum.rollapply(x), 
    rsum.sapply(x), 
    rsum.filter(x), 
    rsum.cumsum(x) 
) 

# Unit: microseconds 
#    expr  min  lq median   uq  max neval 
# rsum.rollapply(x) 12891.315 13267.103 14635.002 17081.5860 28059.998 100 
#  rsum.sapply(x) 4287.533 4433.180 4547.126 5148.0205 12967.866 100 
#  rsum.filter(x) 170.165 208.661 269.648 290.2465 427.250 100 
#  rsum.cumsum(x) 97.539 130.289 142.889 159.3055 449.237 100 

też sobie wyobrazić wszystkie metody będzie szybciej, jeśli x i wszystkie były stosowane ciężary całkowite zamiast numerycznych.

18

Przygotowałem pakiet do obsługi tego typu funkcji "roll", które oferują funkcjonalność podobną do zoo z rollapply, ale z Rcpp na zapleczu. Sprawdź RcppRoll na CRAN.

library(microbenchmark) 
library(zoo) 
library(RcppRoll) 

x <- rnorm(1E5) 

all.equal(m1 <- rollapply(x, 3, sum), m2 <- roll_sum(x, 3)) 

## from flodel 
rsum.cumsum <- function(x, n = 3L) { 
    tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1) 
} 

microbenchmark(
    unit="ms", 
    times=10, 
    rollapply(x, 3, sum), 
    roll_sum(x, 3), 
    rsum.cumsum(x, 3) 
) 

daje mi

Unit: milliseconds 
       expr   min   lq  median   uq   max neval 
rollapply(x, 3, sum) 1056.646058 1068.867550 1076.550463 1113.71012 1131.230825 10 
     roll_sum(x, 3) 0.405992 0.442928 0.457642 0.51770 0.574455 10 
    rsum.cumsum(x, 3) 2.610119 2.821823 6.469593 11.33624 53.798711 10 

może okazać się przydatna, gdy prędkość jest problemem.

+1

ładny, +1. Zastanawiam się, czy oparty na Rcpp 'cumsum 'byłby dużo szybszy niż R? Czy twoje funkcje obsługują NA poprawnie? – flodel

+0

Dla cumsum, prawdopodobnie nie - to już prymitywny, a więc prawdopodobnie tylko pętla C.W kwestii NA: to dobra uwaga. Są teraz traktowane niespójnie. Większość operacji zwraca NA, jeśli jeden z elementów w oknie to NA, chociaż sd zwraca NaN. min i max ignorują NA, w przeciwieństwie do R. I chyba "na.option" byłby użytecznym parametrem. –

+0

@KevinUshey: Wielkie dzięki. To naprawdę szybko. – user2834313

Powiązane problemy