Я увеличил ваш пример, добавив 2 order_ids, чтобы посмотреть, что произойдет, если будет более 1 идентификатора.
Я создал функцию my_fun, смотрите раздел function.В этой функции я беру start_date и создаю новые start_dates на основе разницы между start_date и end_withs, деленной на продолжительность.Это дает новые даты, которые должны быть созданы.после того, как даты split_start были созданы, даты split_end - просто упражнение заполнения, стараясь вычесть 1 день, чтобы добраться до выходных данных вашего примера.
Использование map2
с setNames
и bind_rows
создаетdata.frame с новыми датами и order_id в качестве идентификатора.затем его можно объединить с исходной таблицей для создания ожидаемого результата.
Редактировать:
добавлен код для работы с граничными случаями, такими как NA
в датах или когда start_date равен end_with.
library(lubridate)
library(dplyr)
library(purrr)
df_actual %>%
inner_join(map2(df_actual$start_date, df_actual$ends_with, my_fun) %>%
setNames(., df_actual$order_id) %>%
bind_rows(., .id = "order_id"))
order_id start_date ends_with split_start split_end
1 a 2017-05-01 <NA> <NA> <NA>
2 b 2016-05-01 2016-07-06 2016-05-01 2016-05-30
3 b 2016-05-01 2016-07-06 2016-05-31 2016-06-29
4 b 2016-05-01 2016-07-06 2016-06-30 2016-07-06
5 c 2017-07-01 2017-07-01 2017-07-01 2017-07-01
6 d <NA> 2017-07-01 <NA> <NA>
функция:
my_fun <- function(x, y, duration = 30) {
d <- ddays(duration)
if(is.na(x) | is.na(y)) split_start <- split_end <- NA else
if(x == y) {
split_start <- x
split_end <- y
} else {
n <- (y - x) %/% d
split_start <- c(rep(x, n + 1))
for(i in 1:n+1){
split_start[i] <- x + (i - 1) * d
}
split_end <- split_start[1:n+1]
split_end <- split_end - ddays(1)
split_end[n+1] <- y
}
df <- data.frame(split_start, split_end)
return(df)
}
данные:
df_actual <- structure(list(order_id = c("a", "b", "c", "d"), start_date = structure(c(17287,
16922, 17348, NA), class = "Date"), ends_with = structure(c(NA,
16988, 17348, 17348), class = "Date")), class = "data.frame", row.names = c(NA,
-4L))