Как векторизовать цикл, который для каждой строки суммирует функцию времени, прошедшего между текущей и всеми предыдущими записями этого идентификатора объекта в r - PullRequest
1 голос
/ 19 сентября 2019

У меня есть большая таблица данных (около 900 тыс. Строк), которую можно представить в следующем примере:

        row.id entity.id event.date result
 1:      1       100 2015-01-20     NA
 2:      2       101 2015-01-20     NA
 3:      3       104 2015-01-20     NA
 4:      4       107 2015-01-20     NA
 5:      5       103 2015-01-23     NA
 6:      6       109 2015-01-23     NA
 7:      7       102 2015-01-23     NA
 8:      8       101 2015-01-26     NA
 9:      9       110 2015-01-26     NA
10:     10       112 2015-01-26     NA
11:     11       109 2015-01-26     NA
12:     12       130 2015-01-29     NA
13:     13       100 2015-01-29     NA
14:     14       127 2015-01-29     NA
15:     15       101 2015-01-29     NA
16:     16       119 2015-01-29     NA
17:     17       104 2015-02-03     NA
18:     18       101 2015-02-03     NA
19:     19       125 2015-02-03     NA
20:     20       130 2015-02-03     NA

По существу, у меня есть столбцы, содержащие: идентификатор, представляющий рассматриваемый объект (entity.id).);дата события, в котором участвовал этот идентификатор (обратите внимание, что в каждом событии будет участвовать много разных организаций).Мне нужно рассчитать коэффициент, который для каждого entity.id на каждую дату события зависит (нелинейно) от времени (в днях), которое прошло с момента всех предыдущих событий, в которые был введен этот идентификатор объекта.

Чтобы выразить это другими, более программными терминами, в каждой строке таблицы данных. Мне нужно найти все экземпляры с совпадающим идентификатором, где дата старше, чем дата события рассматриваемой строки, работаетвыведите разницу во времени (в днях) между «текущими» и историческими событиями и суммируйте некоторую нелинейную функцию, примененную к каждому из периодов времени (в этом примере я буду использовать квадрат).

В приведенном выше примере для entity.id = 101 03-02-2015 (строка 18) нам нужно будет вернуться к предыдущим записям этого идентификатора в строках 15, 8 и 2, рассчитать разницу в днях от 'текущее событие (14, 8 и 5 дней), а затем вычислите ответ суммированием квадратов этих периодов (14 ^ 2 + 8 ^ 2 + 5 ^ 2) = 196 + 64 + 25 = 285. (Действительная функциянесколько сложнее, но это достаточно представительно.)

Это тривиально для достижения с помощью циклов for, как показано ниже:

# Create sample dt
dt <- data.table(row.id = 1:20,
     entity.id = c(100, 101, 104, 107, 103, 109, 102, 101, 110, 112,
                   109, 130, 100, 127, 101, 119, 104, 101, 125, 130),
     event.date = as.Date(c("2015-01-20", "2015-01-20", "2015-01-20", "2015-01-20", 
                    "2015-01-23", "2015-01-23", "2015-01-23",
                    "2015-01-26", "2015-01-26", "2015-01-26", "2015-01-26",
                    "2015-01-29", "2015-01-29", "2015-01-29", "2015-01-29", "2015-01-29",
                    "2015-02-03", "2015-02-03", "2015-02-03", "2015-02-03")),
     result = NA)
setkey(dt, row.id)

for (i in 1:nrow(dt)) { #loop through each entry

  # get a subset of dt comprised of rows with this row's entity.id, which occur prior to this row
  event.history <- dt[row.id < i & entity.id == entity.id[i]]

  # calc the sum of the differences between the current row event date and the prior events dates, contained within event.history, squared
  dt$result[i] <- sum( (as.numeric(dt$event.date[i]) - as.numeric(event.history$event.date)) ^2 )
}

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

Обратите внимание, что я создал столбец row.id, чтобы позволить мнеизвлекать все предыдущие строки (а не предыдущие даты), так как они в целом эквивалентны (организация не может посещать более одного события в день), и этот способ был намного быстрее (я думаю, потому что он избегает необходимости приводить даты к числовым значениям доделать сравнение, т. е. Dt [as.numeric (event_date)

Обратите внимание, что я не предан тому, чтобы быть data.table;с удовольствием используйте dplyr или другие механизмы для достижения этой цели, если это будет необходимо.

1 Ответ

1 голос
/ 19 сентября 2019

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

dt[, result2 := dt[
                   dt,
                   on=c("entity.id","event.date<event.date"),
                   sum(as.numeric(x.event.date - i.event.date)^2), by=.EACHI]$V1
                  ]
dt

Это дает результат, который соответствует вашему выводу из цикла, за исключением NA значения:

#    row.id entity.id event.date result result2
# 1:      1       100 2015-01-20      0      NA
# 2:      2       101 2015-01-20      0      NA
# 3:      3       104 2015-01-20      0      NA
# 4:      4       107 2015-01-20      0      NA
# 5:      5       103 2015-01-23      0      NA
# 6:      6       109 2015-01-23      0      NA
# 7:      7       102 2015-01-23      0      NA
# 8:      8       101 2015-01-26     36      36
# 9:      9       110 2015-01-26      0      NA
#10:     10       112 2015-01-26      0      NA
#11:     11       109 2015-01-26      9       9
#12:     12       130 2015-01-29      0      NA
#13:     13       100 2015-01-29     81      81
#14:     14       127 2015-01-29      0      NA
#15:     15       101 2015-01-29     90      90
#16:     16       119 2015-01-29      0      NA
#17:     17       104 2015-02-03    196     196
#18:     18       101 2015-02-03    285     285
#19:     19       125 2015-02-03      0      NA
#20:     20       130 2015-02-03     25      25
...