Я использовал слегка измененные образцы данных, чтобы убедиться, что интервалы, которые находятся на расстоянии <= 60 дней друг от друга, объединяются, как описано в вопросе .. </p>
образец данных
names <- c("John", "John", "Rick", "Rick", "Katie", "Katie", "Harry", "Harry" )
date1 <- c("1-3-2016", "28-4-2020", "13-1-2018", "4-2-2020", "5-1-2019", "29-1-2020", "27-8-2018", "4-2-2020")
date2 <- c("16-4-2020", "28-5-2020", "2-3-2020", "16-2-2020", "25-2-2020", "10-4-2020", "27-6-2019", "8-4-2020")
df1 <- data.frame(names,date1,date2)
names date1 date2
1 John 1-3-2016 16-4-2020
2 John 28-4-2020 28-5-2020 # !! <-- altered so interval-gap with line 1 <= 60 days
3 Rick 13-1-2018 2-3-2020
4 Rick 4-2-2020 16-2-2020
5 Katie 5-1-2019 25-2-2020
6 Katie 29-1-2020 10-4-2020
7 Harry 27-8-2018 27-6-2019
8 Harry 4-2-2020 8-4-2020
names <- c("John", "Rick", "Katie", "Harry", "Harry")
date1 <- c("1-3-2016", "13-1-2018", "5-1-2019", "27-8-2018", "4-2-2020")
date2 <- c("28-5-2020", "2-3-2020", "10-4-2020", "27-6-2019", "8-4-2020")
df2 <- data.frame(names,date1,date2)
names date1 date2
1 John 1-3-2016 28-5-2020 # !! <-- joined, since gap <= 60 days
2 Rick 13-1-2018 2-3-2020 # !! <-- fixed type in your sample data provided
3 Katie 5-1-2019 10-4-2020
4 Harry 27-8-2018 27-6-2019
5 Harry 4-2-2020 8-4-2020
код
В коде используются пакеты data.table
и intervals
. Поскольку intervals
работает только с числовыми c (целыми или действительными) интервалами, столбцы даты преобразуются в числовые c перед созданием / расширением / -слиянием интервалов и возвращаются в формат даты после обработки.
Что делает код ниже:
- L oop (
lapply()
по частям разделения (с использованием data.table::split()
с by-argument
., По имени. keep.by = FALSE
используется, поскольку он нам не нужен, и имя также сохраняется в именах созданного списка. - Для каждого cunck (= name) определяет интервалы на основе двух столбцов даты, и расширить эти интервалы на половину значения переменной
gap
(в приведенном ниже коде установлено значение 60). Таким образом, каждый интервал увеличивается на 30 дней до и после. Затем он объединяет перекрывающиеся / соприкасающиеся (расширенные) интервалы и, наконец, удаляет расширения. - Используйте
data.table::rbindlist()
, чтобы объединить все результаты вместе. - установить имена столбцов и преобразовать числа c значения относятся к формату данных
.
library( data.table )
library( intervals )
#set maximum gap between intervals
gap = 60
#set data to data.table format
setDT(df1)
#set dates to numeric (required by the intervals-package)
df1[, c("date1", "date2") := lapply( .SD, as.numeric ), .SDcols = c("date1", "date2") ]
#where the magic happens (see text above for explanation )
ans <- data.table::rbindlist(
lapply( split( df1 , by = "names", keep.by = FALSE ), function(x) {
as.data.table(
intervals::close_intervals( intervals::contract( intervals::reduce( intervals::expand(
intervals::Intervals( x, type = "Z" ),
gap/2 ) ), gap/2 )
)
)
}),
use.names = TRUE, idcol = "name" )
#use names from df1
setnames( ans, names(ans), names(df1) )
#set numeric back to date
ans[, c("date1", "date2") := lapply( .SD, as.Date, origin = "1970-01-01" ), .SDcols = c("date1", "date2") ]
вывод
names date1 date2
1: John 2016-03-01 2020-05-28
2: Rick 2018-01-13 2020-03-02
3: Katie 2019-01-05 2020-04-10
4: Harry 2018-08-27 2019-06-27
5: Harry 2020-02-04 2020-04-08