Привет, я изо всех сил пытаюсь отличить, когда конкретное событие произошло в моих данных дважды. Скажем, например, что у меня есть 3 типа событий - a,b,c - и каждое из них действует в течение 3 периодов.
Я хочу создать периодическую серию активных событий, в которой любое изменение события заменяет предыдущее событие, но если текущее событие повторяется, оно перезапускается. Вот код для иллюстрации:
a1 <- data.table(
c(1, 4, 7, 8),
c("a", "a", "c", "d")
)
a2 <- data.table(
c(1, 7, 8),
c("a", "c", "d")
)
b <- data.table(
seq(1, 8)
)
setkey(a1, V1)
setkey(a2, V1)
setkey(b, V1)
a1[b, roll = 2]
a2[b, roll = 2]
Как вы можете видеть с a1, я получаю мешанину событий типа a, и я не получаю много информации о том, где действительно повторяется событие a. В идеале я хотел бы, чтобы мое присоединение квалифицировало события как таковые:
data.table(
c(1:8),
c("a", "a", "a", "a", "a", "a", "c", "d"),
event = c(1, 0, 0, 1, 0, 0, 1, 1)
)
Есть идеи? Спасибо!!!
просто для a1, a2 больше для иллюстрации проблемы
Вы можете присоединиться к a1
: a1[b, roll = 2][a1, on = 'V1', event := +!is.na(i.V1)]
IceCreamToucan это идеально! Спасибо
Повторите вектор 1,0,0 до длины .N для каждой группы, используя rleid
для формирования групп.
k <- 3 # restart after this number of events that are the same
one.zeros <- rep(1:0, c(1, k-1)) # length k vector. For k=3, c(1,0,0)
DT[, V3 := rep(one.zeros, length = .N), by = rleid(V2)]
давая:
> DT
V1 V2 V3
1: 1 a 1
2: 2 a 0
3: 3 a 0
4: 4 a 1
5: 5 a 0
6: 6 a 0
7: 7 c 1
8: 8 c 0
9: 9 d 1
Вход DT
:
library(data.table)
DT <- data.table(1:9, c("a", "a", "a", "a", "a", "a", "c", "c", "d"))
это решение кажется интересным, но я не совсем уверен, как обобщить. Когда я применяю его к временному ряду, в котором мои события длятся > 2 периодов, кажется, что он по-прежнему помечает событие, происходящее каждый 3-й период (хотя это не так)
да, это верно, но это микрокосм моей проблемы, поэтому мне просто нужно понять, что такое рычаг, чтобы изменить этот период с 3 на 30, например. Вектор меняется? Или это другой вход в код.
Он присваивает 1 первому элементу группы, затем присваивает 0 второму и третьему, затем снова начинает с 1 четвертого элемента и так далее. Я изменил пример в примечании к своему ответу, чтобы вы могли видеть, что он правильно работает для групп длины 2.
@codesaurus Чтобы расширить c(1, 0, 0)
до вектора длины 30 с 1 в первой позиции, есть replace(integer(30), 1L, 1L)
спасибо, подход кажется очень крутым, я, вероятно, вернусь к нему, если у меня возникнут проблемы с производительностью, пока я использую версию для присоединения
Обобщили, используя k
.
Вы можете проверить, совпали ли ваши ключи (V1
в вашем примере) во время присоединения:
a1[b, .(V2 = x.V2, event = isTRUE(x.V1 == i.V1)), roll = 2, by = .EACHI]
V1 V2 event
1: 1 a TRUE
2: 2 a FALSE
3: 3 a FALSE
4: 4 a TRUE
5: 5 a FALSE
6: 6 a FALSE
7: 7 c TRUE
8: 8 d TRUE
a2[b, .(V2 = x.V2, event = isTRUE(x.V1 == i.V1)), roll = 2, by = .EACHI]
V1 V2 event
1: 1 a TRUE
2: 2 a FALSE
3: 3 a FALSE
4: 4 <NA> FALSE
5: 5 <NA> FALSE
6: 6 <NA> FALSE
7: 7 c TRUE
8: 8 d TRUE
Принимая во внимание комментарии в этом ответе:
set.seed(5438L)
n <- 1e5
a <- data.table(
sample(2 * n, n, replace = FALSE),
sample(c("a", "b", "c"), n, replace = TRUE),
key = "V1"
)
b <- data.table(1:(2 * n), key = "V1")
library(microbenchmark)
microbenchmark(
during = a[b, c(.SD, event = isTRUE(x.V1 == i.V1)), roll = 2, by = .EACHI],
after = a[b, roll = 2][a, event := !is.na(i.V1), on = "V1"],
times = 30L
)
Unit: milliseconds
expr min lq mean median uq max neval cld
during 767.49338 771.49878 795.02283 776.20243 787.96382 964.11575 30 b
after 26.14068 26.46543 28.58425 27.51831 29.73552 37.36052 30 a
Так что ответ IceCreamToucan, вероятно, лучше в этом случае.
нравится это решение, так как оно выполняется в процессе объединения - спасибо! Любые мысли о производительности выполнения этого соединения по сравнению с использованием rleid для назначения событий в результирующем DT, как это предлагается в некоторых других ответах?
Чтобы не вводить имена в случае с большим количеством столбцов, вы можете использовать c(.SD, event = isTRUE(x.V1 == i.V1))
вместо .(V2 = x.V2, event = isTRUE(x.V1 == i.V1))
.
@codesaurus Я также не уверен, что версии rleid
обобщаются, но если они это делают, они могут быть быстрее. Не уверен, интересно было бы проверить. Насколько мне известно, data.table
оценивает j
для каждого совпадения в объединении для экономии памяти, но это может привести к снижению скорости (вероятно, из-за накладных расходов при оценке R-кода каждый раз). Поскольку ваша проблема в основном состоит из одного совпадения для каждого ключа, может быть лучше изменить пост-соединение. Если это так, ответ @IcecreamToucan в комментариях OP может быть быстрее.
Я должен был упомянуть, что, хотя вариант c(.SD,
легче набирать/читать (согласно моим субъективным предпочтениям), он также медленнее, чем ваш исходный ответ.
@IceCreamToucan в вашем комментарии есть +!is.na(i.V1)
, это первый +
специально?
Да, это (небрежный) способ преобразования в числовой. Без него вы получите TRUE
s вместо 1
s
Я попробовал более быструю версию, но она выдает ошибку «объединение приводит к более чем 2 ^ 31 строкам (внутренний vecseq достиг физического предела)» в большом наборе данных. Добавление by = .EACHI решает эту проблему, но затем, похоже, делает ерунду ... какие-либо предложения?
@codesaurus Что именно ты имеешь в виду под ерундой? В данном случае в каждой строке было только одно совпадение, поэтому объединение не должно иметь больше строк, чем вход. Есть ли в вашем большом наборе данных повторяющиеся ключи?
Другой вариант ответа @GaborGrothendieck:
DT[, v := 0L]
DT[(rowid(rleid(V2)) - 1L) %% 3 == 0, v := 1L][]
V1 V2 v
1: 1 a 1
2: 2 a 0
3: 3 a 0
4: 4 a 1
5: 5 a 0
6: 6 a 0
7: 7 c 1
8: 8 c 0
9: 9 d 1
Это просто выполняет арифметические действия над rowid
(1,2,3...) внутри каждой rleid
группы.
Я не уверен, что полностью понял намерения ОП, стоящие за вопросом, но я считаю, что это можно решить альтернативно без.
Если я правильно понимаю, цель - расширить список событий
V1 V2 1: 1 a 2: 4 a 3: 7 c 4: 8 d
в период серия активных событий
V1 V2 event 1: 1 a 1 2: 2 a 0 3: 3 a 0 4: 4 a 1 5: 5 a 0 6: 6 a 0 7: 7 c 1 8: 8 d 1
Во входной таблице data.table V1
обозначает индекс времени, когда произошло событие типа V2
. Итак, мы можем повторять каждое значение V2
до следующего события. Столбец event
создается путем сравнения позиций, указанных в a1$V1
, с последовательностью индексов времени:
a1[, .(1:max(V1), rep(V2, c(diff(V1), 1)), event = +(1:max(V1) %in% V1))]
V1 V2 event 1: 1 a 1 2: 2 a 0 3: 3 a 0 4: 4 a 1 5: 5 a 0 6: 6 a 0 7: 7 c 1 8: 8 d 1
(Обратите внимание, что 1:max(V1) %in% V1
имеет логический тип, +(1:max(V1) %in% V1)
приводит его к целому числу.)
Кроме того, есть два варианта, которые возвращают один и тот же результат, но могут отличаться по производительности (не тестировались):
a1[, .(1:max(V1), rep(V2, c(diff(V1), 1)), event = replace(rep(0L, max(V1)), V1, 1L))]
использует replace()
для создания столбца event
.
a1[, .(1:max(V1), rep(V2, c(diff(V1), 1)), event = 0L)][a1$V1, event := 1L][]
инициализирует столбец event
, но выборочно обновляет его на следующем шаге.
Вам нужен этот ответ для обоих соединений?