Сравните два фрейма данных, чтобы заполнить диапазон дней в r

#r #dataframe #data.table #match

Вопрос:

У меня есть два фрейма данных DF1 и DF2, мне нужно сравнить дни из DF1 по столбцам НИЗКОГО диапазона и высокого диапазона из DF2 и получить столбец диапазона дней в результирующем фрейме данных.

 Items=c("Vegetables","Fruits","Grocery","Dairy Product")
Days=c(16,5,41,25)
DF1=data.frame(Items,Days)

Low_Range=c(0,8,15,22,31,61)
Hi_Range=c(7,14,21,30,60,90)
Days_Range=c("within 7 days","8 to 14 days","15 to 21 days","22 to 30 days","31 to 60 days","61 to 90 days")
DF2=data.frame(Low_Range,Hi_Range,Days_Range)

Days_Slot=c("15 to 21 days","within 7 days","31 to 60 days","22 to 30 days")
DF_Result=data.frame(Items,Days,Days_Slot)
 

DF_Result будет моим результирующим фреймом данных с Days_Slot в качестве нового столбца, добавленного в DF1.
Может ли кто-нибудь помочь решить эту проблему

Ответ №1:

Это может быть решено путем обновления в неравном соединении:

 library(data.table)
setDT(DF1)[setDT(DF2), on = .(Days >= Low_Range, Days <= Hi_Range), 
           Days_Slot := Days_Range][]
 
            Items Days     Days_Slot
1:    Vegetables   16 15 to 21 days
2:        Fruits    5 within 7 days
3:       Grocery   41 31 to 60 days
4: Dairy Product   25 22 to 30 days
 

Обратите внимание, что DF1 это обновляется по ссылке, т. е. новый столбец Days_Slot добавляется DF1 без копирования объекта.


Поскольку интервалы являются непрерывными, соответствие Days_Range может быть определено также с помощью подвижного соединения:

 library(data.table)
setDT(DF1)
setDT(DF2)
DF1[, Days_Slot := DF2[DF1, on = .(Low_Range = Days), roll = TRUE]$Days_Range][]
 
            Items Days     Days_Slot
1:    Vegetables   16 15 to 21 days
2:        Fruits    5 within 7 days
3:       Grocery   41 31 to 60 days
4: Dairy Product   25 22 to 30 days
 

Опять же, новый столбец Days_Slot добавляется DF1 по ссылке.

Кстати, соединение с обратным наклоном даст тот же результат:

 DF1[, Days_Slot := DF2[DF1, on = .(Hi_Range = Days), roll = -Inf]$Days_Range][]
 

Ответ №2:

Вы можете использовать fuzzyjoin .

 fuzzyjoin::fuzzy_left_join(DF1, DF2,
                           by = c('Days' = 'Low_Range', 'Days' = 'Hi_Range'), 
                           match_fun = c(`>=`, `<=`))

#          Items Days Low_Range Hi_Range    Days_Range
#1    Vegetables   16        15       21 15 to 21 days
#2        Fruits    5         0        7 within 7 days
#3       Grocery   41        31       60 31 to 60 days
#4 Dairy Product   25        22       30 22 to 30 days
 

Если ваш набор данных огромен, вы также можете попробовать data.table .

 library(data.table)
setDT(DF1)
setDT(DF2)

DF2[DF1, on = .(Low_Range <= Days, Hi_Range >= Days)]