按月线性分配金额

Tyl*_*den 1 r

请考虑以下合成数据框:

#Learning to enable splitting contributions spanning two months

start = c(as.Date("2013-01-01"), as.Date("2013-02-01"), as.Date("2013-04-01"), as.Date("2013-04-16"), as.Date("2013-05-16"))
end = c(as.Date("2013-01-31"), as.Date("2013-03-31"), as.Date("2013-04-15"), as.Date("2013-05-15"), as.Date("2013-05-31"))
amount = c(100, 200, 50, 100, 50)

df = data.frame(start,end,amount)
Run Code Online (Sandbox Code Playgroud)

这是收到的现金及其相关时间段的清单。其中一些时间段跨越两个月。我想按月汇总。对于与跨越两个月的时期相关的那些金额,我想在两个月之间线性分配/分配它们。

在 R 中执行此操作的惯用正确方法是什么?

G. *_*eck 5

创建一个函数explode,将一个区间分解为每天一行的数据框。使用Map适用explode于每一间隔产生的数据帧,每间隔一个的列表。接下来rbind将列表中的数据框合并为一个大数据框,by.date每天一排。最后汇总by.date成每一年/月的一行:

library(zoo) # as.yearmon

explode <- function(start, end, amount) {
   dates <- seq(start, end, "day")
   data.frame(dates, yearmon = as.yearmon(dates), amount = amount / length(dates))
}
by.date <- do.call("rbind", Map(explode, df$start, df$end, df$amount))
aggregate(amount ~ yearmon, by.date, sum)
Run Code Online (Sandbox Code Playgroud)

使用问题中的数据(假设 2010 年应该是 2013 年),我们得到:

   yearmon    amount
1 Jan 2013 100.00000
2 Feb 2013  94.91525
3 Mar 2013 105.08475
4 Apr 2013 100.00000
5 May 2013 100.00000
Run Code Online (Sandbox Code Playgroud)

更新:如果内存有问题,请explode改用它。它在explodefirst内聚合,因此其输出较小。我们还删除了dates列,DF因为它仅用于调试:

explode <- function(start, end, amount) {
   dates <- seq(start, end, "day")
   DF <- data.frame(yearmon = as.yearmon(dates), amount = amount / length(dates))
   aggregate(amount ~ yearmon, DF, sum)
}
Run Code Online (Sandbox Code Playgroud)

更新 2:这是另一个尝试。它使用rowsumwhich 专门用于汇总和。在我的测试中,这个帖子在数据上的运行速度快了 10 倍。

explode2 <- function(start, end, amount) {
  dates <- seq(start, end, "day")
  n <- length(dates)
  rowsum(rep(amount, n) / n, format(dates, "%Y-%m"))
}
by.date <- do.call("rbind", Map(explode2, df$start, df$end, df$amount))
rowsum(by.date, rownames(by.date))
Run Code Online (Sandbox Code Playgroud)