2013-10-11 2 views
1

Пожалуйста, обратите внимание на следующий синтетический кадр данных:Линейно распределить суммы в месяц

#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

возможно дубликат [Flat РАСПРЕДЕЛЕНИЯ значений по периодам времени] (http://stackoverflow.com/questions/18835463/flat-apportionment-of-values-across-time-periods) – Michele

+0

Благодаря Микеле. Я прочитал ответ на вопрос «Плоский аспект» и не совсем уверен, как я могу применить это к моему примеру. Мне нужно немного больше инструкций, поскольку я совершенно новичок в R, поэтому также нужно некоторое идиоматическое представление о том, как «все делается» в R-образном мышлении. Но спасибо, что указали! –

+0

В случае четвертой строки вы хотите получить то, что выделяет от $ 50 до апреля и $ 50, или оставить это в покое, так как общая продолжительность составляет один месяц? –

ответ

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 

UPDATE: Если проблема с памятью - это использовать вместо 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) 
} 

UPDATE 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)) 
+0

Я нашел этот ответ самым ясным. Я не встречал do.call, но после того, как «потратил немного времени», я начинаю понимать это.Этот ответ также улучшил мое мышление вокруг функционального программирования, но мне еще предстоит пройти долгий путь, прежде чем я смогу по-настоящему понять связанные ответы Flat Apportionment. Большое спасибо всем. –

Смежные вопросы