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

有没有人有更有效的方法来计算基于特定日期而不是天数的移动总和?

4

3 回答 3

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
" )
于 2013-04-27T10:44:13.113 回答
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(因为我更熟悉它)。但根据我的经验,这种解决方案中的“交叉连接”很快就会爆炸,所以如果你有很多行,那将是不可行的。

于 2013-04-28T07:36:44.430 回答
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”。我认为这个答案需要调整,但我不确定。

于 2013-04-28T07:42:28.563 回答