请考虑以下合成数据框:
#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)
这是收到的现金清单及其相关的时间段。其中一些时间跨度为两个月。我想按月汇总一下。对于与跨越两个月的时期相关的金额,我想在两个月之间线性分配/分配。
在R中执行此操作的惯用方法是什么?
答案 0 :(得分:4)
创建一个函数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)
使用问题中的数据(假设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
更新:如果内存有问题,请将其用于explode
。它首先在explode
内聚合,以使其输出更小。我们还删除了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)
}
更新2:这是另一次尝试。它使用专门用于聚合总和的rowsum
。在我的测试中,这篇文章的数据运行速度提高了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))