2013-04-27 81 views
4

我想計算一列移動年度總和的大數據集。它必須是確切的一年,所以我不能將rollapply用作基於特定天數的日期而不是實際日期。根據日期移動總和

作爲一個例子,我有以下代碼:

dates = seq.Date(as.Date('2006-01-01'),as.Date('2007-12-31'),by='days') 
num = 1:length(dates) 
y = cbind(ld,num) 

     ld num 
[1,] 13149 1 
[2,] 13150 2 
[3,] 13151 3 
[4,] 13152 4 
[5,] 13153 5 
[6,] 13154 6 

我想有柱NUM的軋一十年曆史的總和。

我設法解決它的唯一方法是使用循環和數據框的子集。這不是很有效率,我希望有人能夠告訴我如何使用嵌入函數來解釋閏年,因爲它更快。

使用嵌入函數,只要它不是閏年,我就有以下代碼。

b = embed(y[,2],366) 
sums = colSums(b) 

a = ld[length(dates)-365:length(dates)] 
final = cbind(dates = a, rollsum = rev(sums)) 


head(final) 
    dates rollsum 
[1,] 13513 66795 
[2,] 13514 67160 
[3,] 13515 67525 
[4,] 13516 67890 
[5,] 13517 68255 
[6,] 13518 68620 

不要任何人有根據特定的日期計算移動和的更有效的方式,而不是天數?

回答

1

您可以在數據中添加一列,並使用一年前的日期 (佔閏年),並使用sqldf來計算滾動總和。

# Sample data 
dates <- seq.Date(as.Date('2006-01-01'),as.Date('2007-12-31'),by='days') 
d <- data.frame(date = dates, value = rnorm(length(dates))) 
#d <- d[ sample(length(dates), length(dates)/2), ] # For more irregular data 
d <- d[ order(d$date), ] 

# Compute the date one year ago (you can also use lubridate, for date arithmetic) 
d$previous_year <- sapply( 
    d$date, 
    function(u) as.character(seq(u, length=2, by="-1 years")[2]) 
) 
d$date <- as.character(d$date) 

# Compute the rolling sum 
library(sqldf) 
sqldf(" 
    SELECT A.date   AS date, 
     SUM(B.value) AS sum, 
     MIN(B.date) AS start, 
     MAX(B.date) AS end, 
     COUNT(*)  AS observations 
    FROM d A, d B 
    WHERE A.previous_year < B.date AND B.date <= A.date 
    GROUP BY A.date 
") 
0

這應該迅速開展工作,儘管它仍然使用一個循環:

library(data.table) 
library(mondate) 

# Create table with sample dates: 
dt<-data.table(dates = seq.Date(as.Date('2006-01-01'),as.Date('2012-12-31'),by='days'),key="dates") 

# Generate some sample values to be summed, initialize the rolling sum values, and add the row number: 
set.seed(6540) 
dt[,c("val","valroll","rowid"):=list(sample((1L:1e6L)-1L,.N),0L,1:.N)] 

# Subtract one year (12 months) from each date, then subtract that from the original date to get the number of days 
# Create a column to indicate the starting row number to sum from: 
dt[,rowid_lag:=pmax.int(1,rowid-as.integer(dates-as.Date(mondate(dates) - 12)))] 

# For each row, sum from row rowid_lag to rowid: 
for(i in 1:nrow(dt)) { 
    #dt[i,valroll:=dt[dt[i,rowid_lag:rowid],sum(val)]] 
    set(dt, i, "valroll", dt[dt[i,rowid_lag:rowid],sum(val)]) 
} 
rm(i) 

上述假定有沒有日期的任何空白。如果這不是一個好的假設,應該可以調整答案。

使用嵌入是有趣的 - 我以前沒有聽說過。我開始了這條路,但當我無法弄清楚如何處理第365行時,我決​​定回到循環。我會盡力完成該解決方案併發布,以防萬一。

我也考慮過@VincentZoonekynd採取的路線,雖然使用的是data.table而不是sqldf(因爲我更熟悉它)。但根據我的經驗,這種類型的解決方案中的「交叉連接」很快就會爆炸,所以如果你有很多行,這將是不可行的。

0

這個答案使用embed,但它可能不會得到預期的結果爲第366行:

library(data.table) 
library(mondate) 

# Create table with sample dates: 
dt2<-data.table(dates = seq.Date(as.Date('2006-01-01'),as.Date('2012-12-31'),by='days'),key="dates") 

# Generate some sample values to be summed, initialize the rolling sum values, add the row number, and determine the number of days between each date at the prior year (365 or 366): 
set.seed(6540) 
dt2[,c("val","valroll","rowid","lag"):=list(sample((1L:1e6L)-1L,.N),0L,1:.N,as.integer(dates-as.Date(mondate(dates)-12)))] 

# Create a table with column values made up of each of the preceding 366 rows: 
dt2b<-data.table(embed(dt2[,val],366)) 

# Set the 366th column to 0 if the prior year was 365 days ago: 
dt2b[dt2[(dt2[lag-rowid==0L,rowid]+1L):nrow(dt2),lag]==365L,V366:=0L] 

# Sum the rows of the second table, and add the result to the first table: 
dt2[(dt2[lag-rowid==0L,rowid]+1L):nrow(dt2),valroll:=as.integer(rowSums(dt2b))] 
rm(dt2b) 

而且,從我的其他答案(使用for循環)的「valroll」列包含一個額外的與此答案相比較的「val」行。我認爲這個答案需要調整,但我不確定。