Значение в пределах диапазона
У меня есть два кадра данных, которые я хотел бы сравнить.
instances <- data.frame(id = c("AED","AED","CFR","DRR","DRR","DRR","UN","PO"),
dates = as.POSIXct(c("2018-05-17 09:52:00","2018-05-17 10:49:00","2018-05-17 10:38:00","2018-05-17 11:29:00","2018-05-17 12:12:00","2018-05-17 13:20:00","2018-05-17 14:28:00","2018-05-17 15:59:00")))
ranges <- data.frame(id = c("AED","CFR","DRR","DRR","UN"),
start = as.POSIXct(c("2018-05-17 10:00:00","2018-05-17 10:18:00","2018-05-17 11:18:00","2018-05-17 13:10:00","2018-05-17 14:18:00")),
end = as.POSIXct(c("2018-05-17 11:56:00","2018-05-17 12:23:00","2018-05-17 12:01:00","2018-05-17 14:18:00",NA)))
По идентификатору я хочу сравнить каждую дату в фрейме данных экземпляров с соответствующими диапазонами дат, перечисленными в фрейме данных диапазонов. Если в кадре данных диапазонов нет совпадающего идентификатора, он должен возвращаться как FALSE, а если диапазон $end равен NA, он также должен возвращать FALSE. Результат должен быть следующим:
result <- data.frame(id = c("AED","AED","CFR","DRR","DRR","DRR","UN","PO"),
dates = c("2018-05-17 09:52:00","2018-05-17 10:49:00","2018-05-17 10:38:00","2018-05-17 11:29:00","2018-05-17 12:12:00","2018-05-17 13:20:00","2018-05-17 14:28:00","2018-05-17 15:59:00"),
inRange = c(FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE),
outsideRange = c(TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE))
2 ответа
library(dplyr)
instances %>%
full_join(ranges) %>%
mutate(inRange = case_when(dates >= start & dates <= end ~ T, T ~ F))
id dates start end inRange
1 AED 2018-05-17 09:52:00 2018-05-17 10:00:00 2018-05-17 11:56:00 FALSE
2 AED 2018-05-17 10:49:00 2018-05-17 10:00:00 2018-05-17 11:56:00 TRUE
3 CFR 2018-05-17 10:38:00 2018-05-17 10:18:00 2018-05-17 12:23:00 TRUE
4 DRR 2018-05-17 11:29:00 2018-05-17 11:18:00 2018-05-17 12:01:00 TRUE
5 DRR 2018-05-17 11:29:00 2018-05-17 13:10:00 2018-05-17 14:18:00 FALSE
6 DRR 2018-05-17 12:12:00 2018-05-17 11:18:00 2018-05-17 12:01:00 FALSE
7 DRR 2018-05-17 12:12:00 2018-05-17 13:10:00 2018-05-17 14:18:00 FALSE
8 DRR 2018-05-17 13:20:00 2018-05-17 11:18:00 2018-05-17 12:01:00 FALSE
9 DRR 2018-05-17 13:20:00 2018-05-17 13:10:00 2018-05-17 14:18:00 TRUE
10 UN 2018-05-17 14:28:00 2018-05-17 14:18:00 <NA> FALSE
11 PO 2018-05-17 15:59:00 <NA> <NA> FALSE
решение data.table
Я бы решил эту проблему, используя функцию foverlaps() из data.table... Единственная проблема заключается в том, что он принимает только полные диапазоны дат, а в предоставленных образцах диапазонах [,5] нет конечной даты...
> ranges
id start end
1 AED 2018-05-17 10:00:00 2018-05-17 11:56:00
2 CFR 2018-05-17 10:18:00 2018-05-17 12:23:00
3 DRR 2018-05-17 11:18:00 2018-05-17 12:01:00
4 DRR 2018-05-17 13:10:00 2018-05-17 14:18:00
5 UN 2018-05-17 14:18:00 <NA>
Для следующего решения слова все диапазоны должны иметь начало и конец. Итак, давайте заполним NA, используя временную метку.
ranges <- data.frame(id = c("AED","CFR","DRR","DRR","UN"),
start = as.POSIXct(c("2018-05-17 10:00:00","2018-05-17 10:18:00","2018-05-17 11:18:00","2018-05-17 13:10:00","2018-05-17 14:18:00")),
end = as.POSIXct(c("2018-05-17 11:56:00","2018-05-17 12:23:00","2018-05-17 12:01:00","2018-05-17 14:18:00", "2018-05-17 16:18:00")))
> ranges
id start end
1 AED 2018-05-17 10:00:00 2018-05-17 11:56:00
2 CFR 2018-05-17 10:18:00 2018-05-17 12:23:00
3 DRR 2018-05-17 11:18:00 2018-05-17 12:01:00
4 DRR 2018-05-17 13:10:00 2018-05-17 14:18:00
5 UN 2018-05-17 14:18:00 2018-05-17 16:18:00
Workflow
library(data.table)
#make instances a data.table without key
instances.dt <- setDT( instances, key = NULL )
#create a data.table with the ranges, set keys
ranges.dt <- setDT( ranges, key = c("id", "start", "end") )
#create a temporary 'range', where start == end, based on the dates-column
instances.dt[, c( "start", "end") := dates]
#create a column 'inRange' using data.table's foverlaps().
#use the secons column of the fovelaps' result. If this column is NA, then no 'hit' was found
#in ranges.dt and inrange == FALSE, else inRange == TRUE
instances.dt[, inRange := !is.na( foverlaps(instances.dt, ranges.dt, type = "within", mult = "first", nomatch = NA)[,2] )]
#outsideRange is the opposite of inRange
instances.dt[, outsideRange := !inRange]
#remove the temporary columns 'start' and 'end'
instances.dt[, c("start", "end") := NULL]
Результат
> instances.dt
id dates inRange outsideRange
1: AED 2018-05-17 09:52:00 FALSE TRUE
2: AED 2018-05-17 10:49:00 TRUE FALSE
3: CFR 2018-05-17 10:38:00 TRUE FALSE
4: DRR 2018-05-17 11:29:00 TRUE FALSE
5: DRR 2018-05-17 12:12:00 FALSE TRUE
6: DRR 2018-05-17 13:20:00 TRUE FALSE
7: UN 2018-05-17 14:28:00 TRUE FALSE
8: PO 2018-05-17 15:59:00 FALSE TRUE
Это работает невероятно быстро, даже для огромных таблиц данных.
Вы можете сократить код, но мне всегда нравится выполнять анализ по одному шагу за раз, улучшая читабельность.
Прикован с помощью трубного оператора магритта
library(data.table)
library(magrittr)
ranges.dt <- setDT( ranges, key = c("id", "start", "end") )
result <- setDT( instances, key = NULL ) %>%
.[, c( "start", "end") := dates] %>%
.[, inRange := !is.na( foverlaps( ., ranges.dt, type = "within", mult = "first", nomatch = NA )[,2] )] %>%
.[, outsideRange := !inRange] %>%
.[, c("start", "end") := NULL]