2013-04-27 85 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”行。我认为这个答案需要调整,但我不确定。