Линейное распределение сумм по месяцам
Пожалуйста, рассмотрите следующий синтетический фрейм данных:
#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?
1 ответ
Создать функцию 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))