Сопоставление временного интервала одного события с другим событием в R - PullRequest
2 голосов
/ 05 августа 2020

Я пытаюсь сделать невозможное? Я хочу сопоставить события в df1 с событиями в df2, если event2 или период в 10 дней до того, как event2 пересечется с датой в event1. Я вставил образцы из двух наборов данных. Я просмотрел и не нашел ничего похожего на этот вопрос на этом форуме, поэтому, возможно, это невозможно. Заранее спасибо!

head(df1)
    # A tibble: 6 x 1
    # Groups:   event1 [6]
      event1
       <date>     
    1 1980-01-10 
    2 1980-01-13 
    3 1980-01-14 
    4 1980-02-18 
    5 1980-02-27 
    6 1980-03-02 

head(df2)

      event2
    1  1980-01-16
    2  1980-01-18
    3  1980-01-19
    4  1980-02-12
    5  1980-09-26
    6  1980-10-23

Я думаю, что мне нужно что-то вроде этого (с использованием первых трех event2):

ev_1 <- interval(ymd('1980-01-06'), ymd('1980-01-16'))
ev_2 <- interval(ymd('1980-01-08'), ymd('1980-01-18')) 
ev_3 <- interval(ymd('1980-01-09'), ymd('1980-01-19'))

Затем я хочу посмотреть, есть ли Даты события event1 имеют место в интервале. В общей сложности у меня есть около 60 дат event2 и сотни дат event1 за 40-летний период.

Я смог придумать это, используя инструкции здесь , но это лучший подход ? Если да, то можно ли автоматизировать это, чтобы мне не приходилось вручную записывать все 60 интервалов?

> dates_test <- ymd(c("1980-01-10", "1980-01-13", "1980-01-14", "1980-02-18"))
> interval_test<- list(interval(ymd('1980-01-06'), ymd('1980-01-16')),
                       interval(ymd('1980-01-09'), ymd('1980-01-19')))
> dates_test %within% interval_test
[1]  TRUE  TRUE  TRUE FALSE

Ответы [ 2 ]

1 голос
/ 08 августа 2020

OP задал два вопроса:

  1. Является ли использование оператора %within% из lubridate лучшим подходом?
  2. Можно ли автоматизировать его так, чтобы 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 интервалов вручную.

Теперь эта проблема решена, поэтому давайте сравним три разных подхода, опубликованных на данный момент, с точки зрения скорости выполнения :

  1. %within% и interval() из lubridate
  2. expand.grid() предложено marcguery
  3. %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 МБ соответственно).

1 голос
/ 05 августа 2020

Вы можете создать все возможные комбинации event1 и event2, а затем сохранить строки, когда event2 пройдет через 10 дней или меньше после event1.

combinations <- expand.grid(df1$event1, df2$event2)
matches <- combinations[combinations[,2] >= combinations[,1] & combinations[,2] - combinations[,1] <= 10,]
matches

         Var1       Var2
1  1980-01-10 1980-01-16
2  1980-01-13 1980-01-16
3  1980-01-14 1980-01-16
7  1980-01-10 1980-01-18
8  1980-01-13 1980-01-18
9  1980-01-14 1980-01-18
13 1980-01-10 1980-01-19
14 1980-01-13 1980-01-19
15 1980-01-14 1980-01-19
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...