2013-10-30 32 views
4

我需要計算涉及條件的數據框中列的滾動行總和。我所擁有的數據對「sku」有多個觀察值。我想要的是爲每個「sku」值計算5個連續行的總和。如果我達到了一個我沒有連續觀察5次「sku」的階段,我們將總結剩餘的行觀察值。在R中有效計算滾動行總和

對於說明性示例考慮以下的數據幀:

data <- structure(list(sku = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 
          2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 
          3L, 3L, 3L, 3L), tf = c(50.79, 46.39, 47.85, 45.79, 44.46, 49.99, 
                46.12, 44.4, 41.21, 53.7, 53.9, 44.91, 59.64, 41.96, 52.26, 43.48, 
                46.93, 51.2, 54.31, 42.5, 47.2, 57.54, 63.23, 48.98, 52.38, 59.9, 
                53.01, 50.35, 41.86, 46.42)), .Names = c("sku", "tf"), row.names = c(NA, 
                -30L), class = "data.frame") 

在該數據幀中,我們要總結的變量「TF」針對每個「SKU」值的5個軋製值。

我們已經能夠完成,使用下面的代碼:

data[,c("day_5")]<-unlist(mapply(function(y){ 
end1<-(which(data$sku==unique(data$sku)[y]))[length(which(data$sku==unique(data$sku)[y]))] 
start<-(which(data$sku==unique(data$sku)[y]))[1] 
d<-data$tf[start:end1] 
r<-mapply(function(x){if (x+4 <= length(d)) {sum(d[x:(x+4)])} else {sum(d[x:length(d)])}},1:length(d)) 
},1:length(unique(data$sku)))) 

列「day_5」給我們正是我們想要的,但是這種方法被證明是非常低效的,因爲我們必須在運行此操作數以百萬計的「sku」值的數據行。

有人可以幫助我們優化此代碼,我們可以將它擴展爲大數據嗎?

+0

'zoo'包有許多滾動功能,包括'rollsum' – Justin

回答

4

對於龐大的數據集,您應該使用package data.table。包動物園提供滾動手段,總和和適用的功能。

library(data.table) 
DT <- data.table(data) 

library(zoo) 
fun <- function(x, i) { 
    x <- c(x, rep(0, i-1)) 
    rollsumr(x, k=i) 
} 

DT[, day_5a:=fun(tf,5), by=sku] 
print(DT) 

#  sku tf day_5 day_5a 
# 1: 1 50.79 235.28 235.28 
# 2: 1 46.39 234.48 234.48 
# 3: 1 47.85 234.21 234.21 
# 4: 1 45.79 230.76 230.76 
# 5: 1 44.46 226.18 226.18 
# 6: 1 49.99 181.72 181.72 
# 7: 1 46.12 131.73 131.73 
# 8: 1 44.40 85.61 85.61 
# 9: 1 41.21 41.21 41.21 
# 10: 2 53.70 254.11 254.11 
# 11: 2 53.90 252.67 252.67 
#<snip> 
1

中借鑑羅納德功能,更簡單的方法可能是使用:

fun <- function(x, i) { 
    x <- c(x, rep(0, i-1)) 
    rollsumr(x, k=i) 
} 
data$day_5_a <- ave(data$tf, data$sku, FUN= function(x) fun(x, 5)) 
+0

我不認爲'ave'更簡單。但是,數百萬觀測數據和數千個小組的數據速度會慢得多。 – Roland

1

只使用base明顯效率較低,比data.table那麼優雅):

data_ls <- split(data, data$sku) 

res <- lapply(data_ls, 
      function(z) sapply(1:length(z$tf), 
       function(vec, x) { sum(vec[x:(x+4)], na.rm = T) }, 
       vec = z$tf)) 

data$day_5 <- unlist(res) 

#> data 
# sku tf day_5 
#1 1 50.79 235.28 
#2 1 46.39 234.48 
#3 1 47.85 234.21 
#4 1 45.79 230.76 
#5 1 44.46 226.18 
#6 1 49.99 181.72 
#7 1 46.12 131.73 
#8 1 44.40 85.61 
#9 1 41.21 41.21 
#10 2 53.70 254.11 
#11 2 53.90 252.67 
#12 2 44.91 242.25 
+0

請不要使用兩個嵌套循環。如果你想留在基地,這是一種可能,然後可以結合功能,如'ave'或'tapply':'fun < - function(x,i)rev(na.omit(filter(c(rep( 0,i-1),rev(x)),rep(1,i),sides = 1)))' – Roland

+0

@Roland:那好!謝謝!我想你應該編輯你的答案,添加一個'base'選項,我會刪除這個方法,而不是使用你的函數編輯它。 –

+0

我不認爲你應該使用這個基地。問題標題要求效率,我不認爲你可以擊敗data.table。 – Roland