OP задал два вопроса:
- Является ли использование оператора
%within%
из lubridate
лучшим подходом? - Можно ли автоматизировать его так, чтобы OP не обязательно писать вручную все 60 интервалов?
Чтобы сначала ответить на второй вопрос: Да, возможно:
%within%
, lapply()
и interval()
OP был почти готов. Согласно документации из a %within% b
,
Если b
- это список интервалов, a
проверяется, попадает ли он в любой интервалов
Мы можем создать список интервалов из заданного вектора дат df2$event2
по
lapply(df2$event2, function(x) interval(x - 10, x))
[[1]]
[1] 1980-01-06 UTC--1980-01-16 UTC
[[2]]
[1] 1980-01-08 UTC--1980-01-18 UTC
[[3]]
[1] 1980-01-09 UTC--1980-01-19 UTC
[[4]]
[1] 1980-02-02 UTC--1980-02-12 UTC
[[5]]
[1] 1980-09-16 UTC--1980-09-26 UTC
[[6]]
[1] 1980-10-13 UTC--1980-10-23 UTC
start
Дата каждого интервала вычисляется из даты end
путем вычитания 10 дней. Итак,
library(lubridate)
df1$event1 %within% lapply(df2$event2, function(x) interval(x - 10, x))
возвращает логический вектор (в соответствии с ожидаемым результатом OP)
[1] TRUE TRUE TRUE FALSE FALSE FALSE
, который может использоваться для подмножества df1
для выбора совпадающие события из df1
в виде вектора дат
df1[df1$event1 %within% lapply(df2$event2, function(x) interval(x - 10, x)), ]
[1] "1980-01-10" "1980-01-13" "1980-01-14"
или
df1[df1$event1 %within% lapply(df2$event2, function(x) interval(x - 10, x)), , drop = FALSE]
, который возвращает подмножество data.frame.
event1
1 1980-01-10
2 1980-01-13
3 1980-01-14
%inrange%
от data.table
Для полноты картины в пакете data.table
есть аналогичный оператор %inrange
:
library(data.table)
setDT(df1)
setDT(df2)
df1[event1 %inrange% df2[, .(event2 - 10L, event2)]]
event1
1: 1980-01-10
2: 1980-01-13
3: 1980-01-14
setDT(df1)
и setDT(df2)
приведение data.frames к объектам data.table.
Benchmarking
Теперь мы можем попытаться ответить на OP первый вопрос, касающийся «лучший подход» .
ОП не указала критерии для оценки подхода как «лучшего». Вероятно, OP был в основном озабочен попыткой написать 60 интервалов вручную.
Теперь эта проблема решена, поэтому давайте сравним три разных подхода, опубликованных на данный момент, с точки зрения скорости выполнения :
%within%
и interval()
из lubridate
expand.grid()
предложено marcguery %inrange%
из data.table
Для тестирования используется пакет bench
, поскольку он измеряет время выполнения, а также выделение памяти для различных размеров проблем. Он также проверяет идентичность результатов. Таким образом, три подхода изменяются, чтобы возвращать вектор дат.
library(bench)
library(ggplot2)
bm <- press(
n1 = c(100L, 1E3L, 1E4L),
n2 = c(10L, 100L, 1000L),
{
beg <- as.Date("1980-01-01")
end <- as.Date("2020-12-31")
df1 <- data.frame(event1 = seq(beg, end, length.out = n1))
df2 <- data.frame(event2 = seq(beg, end, length.out = n2))
dt1 <- as.data.table(df1)
dt2 <- as.data.table(df2)
mark(
within = {
df1[df1$event1 %within% lapply(df2$event2, function(x) interval(x - 10, x)), ]
},
inrange = {
dt1[event1 %inrange% dt2[, .(event2 - 10L, event2)], event1]
},
exp.grid = {
combinations <- expand.grid(df1$event1, df2$event2)
matches <- combinations[combinations[,2] >= combinations[,1] &
combinations[,2] - combinations[,1] <= 10,]
unique(matches[[1L]])
},
check = TRUE
)
}
)
autoplot(bm)
введите описание изображения здесь
Обратите внимание на логарифм c шкалу времени.
Только для задач самого маленького размера метод expand.grid()
является самым быстрым. Для всех других размеров задач (включая случай 1000 event1
и 100 event2
, который близок к размеру задачи OP), data.table
%inrange%
является самым быстрым. Для самого большого случая с 10000 event1
и 1000 event2
, data.table
больше 2 звездных величин быстрее, чем другие подходы.
library(dplyr)
bm %>%
select(1:11) %>%
filter(n1 == max(n1), n2 == max(n2)) %>%
mutate(expression = names(expression) %>% unique())
# A tibble: 3 x 11
expression n1 n2 min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<chr> <int> <int> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 within 10000 1000 780.16ms 780.2ms 1.28 307MB 2.56 1 2 780ms
2 inrange 10000 1000 2.68ms 3.3ms 293. 491KB 0 147 0 502ms
3 exp.grid 10000 1000 834.35ms 834.3ms 1.20 882MB 3.60 1 3 834ms
Кроме того, data.table
выделяет 3 величины меньше памяти (0,5 МБ против 307 МБ или 882 МБ соответственно).