Как пометить, когда скользящие соединения вступают в силу в data.table

Привет, я изо всех сил пытаюсь отличить, когда конкретное событие произошло в моих данных дважды. Скажем, например, что у меня есть 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)
)

Есть идеи? Спасибо!!!

Вам нужен этот ответ для обоих соединений?

akrun 28.06.2019 15:28

просто для a1, a2 больше для иллюстрации проблемы

codesaurus 28.06.2019 15:29

Вы можете присоединиться к a1: a1[b, roll = 2][a1, on = 'V1', event := +!is.na(i.V1)]

IceCreamToucan 28.06.2019 16:06

IceCreamToucan это идеально! Спасибо

codesaurus 28.06.2019 16:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
78
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Повторите вектор 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-й период (хотя это не так)

codesaurus 28.06.2019 16:02

да, это верно, но это микрокосм моей проблемы, поэтому мне просто нужно понять, что такое рычаг, чтобы изменить этот период с 3 на 30, например. Вектор меняется? Или это другой вход в код.

codesaurus 28.06.2019 16:07

Он присваивает 1 первому элементу группы, затем присваивает 0 второму и третьему, затем снова начинает с 1 четвертого элемента и так далее. Я изменил пример в примечании к своему ответу, чтобы вы могли видеть, что он правильно работает для групп длины 2.

G. Grothendieck 28.06.2019 16:13

@codesaurus Чтобы расширить c(1, 0, 0) до вектора длины 30 с 1 в первой позиции, есть replace(integer(30), 1L, 1L)

Frank 28.06.2019 16:14

спасибо, подход кажется очень крутым, я, вероятно, вернусь к нему, если у меня возникнут проблемы с производительностью, пока я использую версию для присоединения

codesaurus 28.06.2019 16:28

Обобщили, используя k.

G. Grothendieck 28.06.2019 16:51
Ответ принят как подходящий

Вы можете проверить, совпали ли ваши ключи (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, как это предлагается в некоторых других ответах?

codesaurus 28.06.2019 16:22

Чтобы не вводить имена в случае с большим количеством столбцов, вы можете использовать c(.SD, event = isTRUE(x.V1 == i.V1)) вместо .(V2 = x.V2, event = isTRUE(x.V1 == i.V1)).

IceCreamToucan 28.06.2019 16:22

@codesaurus Я также не уверен, что версии rleid обобщаются, но если они это делают, они могут быть быстрее. Не уверен, интересно было бы проверить. Насколько мне известно, data.table оценивает j для каждого совпадения в объединении для экономии памяти, но это может привести к снижению скорости (вероятно, из-за накладных расходов при оценке R-кода каждый раз). Поскольку ваша проблема в основном состоит из одного совпадения для каждого ключа, может быть лучше изменить пост-соединение. Если это так, ответ @IcecreamToucan в комментариях OP может быть быстрее.

Alexis 28.06.2019 16:28

Я должен был упомянуть, что, хотя вариант c(.SD, легче набирать/читать (согласно моим субъективным предпочтениям), он также медленнее, чем ваш исходный ответ.

IceCreamToucan 28.06.2019 17:05

@IceCreamToucan в вашем комментарии есть +!is.na(i.V1), это первый + специально?

Alexis 28.06.2019 17:07

Да, это (небрежный) способ преобразования в числовой. Без него вы получите TRUEs вместо 1s

IceCreamToucan 28.06.2019 17:08

Я попробовал более быструю версию, но она выдает ошибку «объединение приводит к более чем 2 ^ 31 строкам (внутренний vecseq достиг физического предела)» в большом наборе данных. Добавление by = .EACHI решает эту проблему, но затем, похоже, делает ерунду ... какие-либо предложения?

codesaurus 12.07.2019 18:04

@codesaurus Что именно ты имеешь в виду под ерундой? В данном случае в каждой строке было только одно совпадение, поэтому объединение не должно иметь больше строк, чем вход. Есть ли в вашем большом наборе данных повторяющиеся ключи?

Alexis 12.07.2019 20:10

Другой вариант ответа @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, но выборочно обновляет его на следующем шаге.

Другие вопросы по теме