我是一个大型数据集,我想计算一列的移动年度总和。它必须是确切的一年,所以我不能使用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
我希望滚动一年的历史总和列数。
我设法解决它的唯一方法是使用循环和数据帧的子集。这不是很有效,我希望有人可以建议我如何使用嵌入函数来解释闰年,因为它更快。
使用embed函数,我有以下代码,只要它不是闰年就可以工作。
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
有没有人能够更有效地根据具体日期而不是天数来计算移动金额?
答案 0 :(得分: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
" )
答案 1 :(得分: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
(因为我对它更熟悉)。但根据我的经验,这种类型的解决方案中的“交叉连接”很快就会爆炸,所以如果你有很多行,那就不可行了。
答案 2 :(得分: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”行,与此答案相比。我认为这个答案需要调整,但我不确定。