Я предполагаю, что у вас есть какое-то уникальное поле идентификатора в вашем наборе данных, поэтому у вас есть уникальная запись. В противном случае это не сработает. Я также добавил 1 дополнительную запись, чтобы мы могли видеть, что все работает на нескольких записях.
Данные:
library(lubridate)
id <- c(1:2) # added id field needed for unique record and needed for grouping
dateStart <- c(lubridate::ymd("2010-01-01", "2011-01-09"))
dateEnd <- c(lubridate::ymd("2010-03-06", "2011-04-09"))
length <- c(65, 91)
Amt <- c(348.80, 468.70)
df1 <- data.frame(id , dateStart, dateEnd, length, Amt)
Сначала создайте data.frame с идентификатором и отсутствующими месяцами. Нам нужны dplyr
, tidyr
и padr
. Создайте группы для уникального идентификатора, gather
даты, чтобы у нас была дата начала и окончания в 1 столбце Чтобы padr
продлил месяцы, нам сначала нужно thicken
data.frame. Избавьтесь от ненужных столбцов и заполните пропущенные месяцы.
library(dplyr)
library(tidyr)
library(padr)
#create last_day function for later use
last_day <- function(date) {
ceiling_date(date, "month") - days(1)
}
dates <- df1 %>%
select(id, dateStart, dateEnd) %>%
group_by(id) %>%
gather(names, dates, -id) %>%
arrange(id, dates) %>%
thicken(interval = "month") %>% # need to thicken first for month interval
select(-c(names, dates)) %>%
pad(interval = "month")
dates
# A tibble: 7 x 2
# Groups: id [2]
id dates_month
<int> <date>
1 1 2010-01-01
2 1 2010-02-01
3 1 2010-03-01
4 2 2011-01-01
5 2 2011-02-01
6 2 2011-03-01
7 2 2011-04-01
Далее присоедините данные к исходному data.frame
df_extended <- inner_join(dates, df1, by = "id")
df_extended
# A tibble: 7 x 6
# Groups: id [2]
id dates_month dateStart dateEnd length Amt
<int> <date> <date> <date> <dbl> <dbl>
1 1 2010-01-01 2010-01-01 2010-03-06 65 349.
2 1 2010-02-01 2010-01-01 2010-03-06 65 349.
3 1 2010-03-01 2010-01-01 2010-03-06 65 349.
4 2 2011-01-01 2011-01-09 2011-04-09 91 469.
5 2 2011-02-01 2011-01-09 2011-04-09 91 469.
6 2 2011-03-01 2011-01-09 2011-04-09 91 469.
7 2 2011-04-01 2011-01-09 2011-04-09 91 469.
Теперь, чтобы добраться до конечного результата. нужно использовать case_when
, ifelse
по какой-то причине не возвращает данные в формате даты. Замена case_when
устанавливает правильные даты начала и окончания (я предполагаю, что вам нужна точная дата начала, а не первое число месяца, в противном случае измените код, чтобы вместо него использовать date_month.) Я создаю переменную суммы в день (amt_pd), чтобы можно умножить это на количество дней в месяце, чтобы получить пропорциональную сумму для количества дней в месяце.
df_end <- df_extended %>%
mutate(dateEnd = case_when(last_day(dates_month) <= dateEnd ~ last_day(dates_month),
TRUE ~ dateEnd),
dateStart = case_when(dates_month <= dateStart ~ dateStart,
TRUE ~ dates_month),
amt_pd = Amt / length,
length = dateEnd - dateStart + 1,
Amt = amt_pd * length) %>%
select(-c(dates_month, amt_pd)) # get rid of not needed columns
df_end
# A tibble: 7 x 5
# Groups: id [2]
id dateStart dateEnd length Amt
<int> <date> <date> <time> <time>
1 1 2010-01-01 2010-01-31 31 166.350769230769
2 1 2010-02-01 2010-02-28 28 150.252307692308
3 1 2010-03-01 2010-03-06 6 32.1969230769231
4 2 2011-01-09 2011-01-31 23 118.462637362637
5 2 2011-02-01 2011-02-28 28 144.215384615385
6 2 2011-03-01 2011-03-31 31 159.667032967033
7 2 2011-04-01 2011-04-09 9 46.354945054945
Все это можно сделать за один раз. Но если у вас есть 15 миллионов строк, возможно, лучше проверить, работают ли промежуточные шаги. Также обратите внимание, что pad
имеет опцию break_above
.
Это числовое значение, которое указывает количество строк в миллионах
выше которого функция сломается. Защитная сеть для ситуаций, когда
интервал отличается от ожидаемого, и заполнение дает очень
большой фрейм данных, возможно переполнение памяти.