Nano Hash - криптовалюты, майнинг, программирование

R - найти последовательность элементов строки на основе временных ограничений в кадре данных

Рассмотрим следующий фрейм данных (отсортированный по идентификатору и времени):

df <- data.frame(id = c(rep(1,7),rep(2,5)), event = c("a","b","b","b","a","b","a","a","a","b","a","a"), time = c(1,3,6,12,24,30,32,1,2,6,17,24))
df
   id event time
1   1     a    1
2   1     b    3
3   1     b    6
4   1     b   12
5   1     a   24
6   1     b   30
7   1     a   42
8   2     a    1
9   2     a    2
10  2     b    6
11  2     a   17
12  2     a   24

Я хочу подсчитать, сколько раз данная последовательность событий появляется в каждой группе «id». Рассмотрим следующую последовательность с ограничениями по времени:

seq <- c("a", "b", "a")
time_LB <- c(0, 2, 12)
time_UB <- c(Inf, 8, 18)

Это означает, что событие «а» может начаться в любой момент, событие «б» должно начаться не ранее 2 и не позднее 8 после события «а», другое событие «а» должно начаться не ранее 12 и не позднее 18 после события «б». Некоторые правила создания последовательностей:

  1. События не обязательно должны быть последовательными относительно столбца «время». Например, seq можно составить из строк 1, 3 и 5.
  2. Для подсчета последовательности должны иметь другое первое событие. Например, если было подсчитано seq = строки 8, 10 и 11, то seq = строки 8, 10 и 12 не должны учитываться.
  3. События могут быть включены во многие построенные последовательности, если они не нарушают второе правило. Например, мы считаем обе последовательности: строки 1, 3, 5 и строки 5, 6, 7.

Ожидаемый результат:

df1
  id count
1  1     2
2  2     2

Есть несколько связанных вопросов в R - Определите последовательность элементов строки по группам в фрейме данных и Поиск строк в кадре данных R, где значение столбца следует за последовательностью.

Это способ решить проблему с помощью «dplyr»?

20.01.2017

  • немного запутался - берем id == 2; Здесь a=1,b=6,a=17 - единственный случай, который удовлетворяет условиям, верно? так что счет должен быть 1, верно? я правильно понял вопрос? 20.01.2017
  • другой seq - это a = 2, b = 6, a = 17 или a = 2, b = 6, a = 24 21.01.2017
  • тогда у нас также может быть a=24, b=6, a=17? 21.01.2017
  • Вы имели в виду a = 2, а не 24? Если да, то правило 2 гласит, что мы можем считать только один из них. 21.01.2017
  • Я все еще в замешательстве. Нет, я имел в виду 24 само по себе; поскольку a может иметь любое значение от 0 до Inf? мы пытаемся искать все перестановки? 21.01.2017
  • Так вас также интересуют все экземпляры seq? Например, я не проверял это, но мне пришло в голову, что у вас могут быть seq, которые перекрываются, или seq, которые встречаются в других seq (хотя idk, если вы это сделаете в своем примере, он может существовать в ваших данных ). Так вы заинтересованы в поиске ВСЕХ секвенций или только взаимоисключающих секвенций? Должна ли одна последовательность заканчиваться до начала другой? 21.01.2017
  • Если a началось в 24, b = 6 не может начаться после a, как объяснено в разделе временных ограничений. 21.01.2017
  • Да, я ищу все последовательности с ограничениями по Правилу 2. 21.01.2017

Ответы:


1

Я считаю, что это то, что вы ищете. Это дает вам желаемый результат. Обратите внимание, что в вашем исходном вопросе есть опечатка, где у вас 32 вместо 42, когда вы определяете столбец time в df. Я говорю, что это опечатка, потому что она не соответствует вашему выводу непосредственно под определением df. Я изменил 32 на 42 в приведенном ниже коде.

library(dplyr)

df <- data.frame(id = c(rep(1,7),rep(2,5)), event = c("a","b","b","b","a","b","a","a","a","b","a","a"), time = c(1,3,6,12,24,30,42,1,2,6,17,24))

seq <- c("a", "b", "a")
time_LB <- c(0, 2, 12)
time_UB <- c(Inf, 8, 18)

df %>% 
  full_join(df,by='id',suffix=c('1','2')) %>% 
  full_join(df,by='id') %>% 
  rename(event3 = event, time3 = time) %>%
  filter(event1 == seq[1] & event2 == seq[2] & event3 == seq[3]) %>% 
  filter(time1 %>% between(time_LB[1],time_UB[1])) %>% 
  filter((time2-time1) %>% between(time_LB[2],time_UB[2])) %>% 
  filter((time3-time2) %>% between(time_LB[3],time_UB[3])) %>%
  group_by(id,time1) %>%
  slice(1) %>%   # slice 1 row for each unique id and time1 (so no duplicate time1s)
  group_by(id) %>% 
  count()

Вот результат:

# A tibble: 2 x 2
     id     n
  <dbl> <int>
1     1     2
2     2     2

Кроме того, если вы опустите последние 2 части канала dplyr, которые производят подсчет (чтобы увидеть совпадающие последовательности), вы получите следующие последовательности:

Source: local data frame [4 x 7]
Groups: id, time1 [4]

     id event1 time1 event2 time2 event3 time3
  <dbl> <fctr> <dbl> <fctr> <dbl> <fctr> <dbl>
1     1      a     1      b     6      a    24
2     1      a    24      b    30      a    42
3     2      a     1      b     6      a    24
4     2      a     2      b     6      a    24

РЕДАКТИРОВАТЬ В ОТВЕТЕ НА КОММЕНТАРИЙ ОТНОСИТЕЛЬНО ОБОБЩЕНИЯ ЭТОГО: Да, это можно обобщить на последовательности произвольной длины, но требует некоторого R voodoo. В частности, обратите внимание на использование Reduce, которое позволяет вам применять общую функцию к списку объектов, а также foreach, которую я заимствую из пакета foreach для выполнения некоторого произвольного цикла. Вот код:

library(dplyr)
library(foreach)

df <- data.frame(id = c(rep(1,7),rep(2,5)), event = c("a","b","b","b","a","b","a","a","a","b","a","a"), time = c(1,3,6,12,24,30,42,1,2,6,17,24))

seq <- c("a", "b", "a")
time_LB <- c(0, 2, 12)
time_UB <- c(Inf, 8, 18)

multi_full_join = function(df1,df2) {full_join(df1,df2,by='id')}
df_list = foreach(i=1:length(seq)) %do% {df} 
df2 = Reduce(multi_full_join,df_list)

names(df2)[grep('event',names(df2))] = paste0('event',seq_along(seq))
names(df2)[grep('time',names(df2))] = paste0('time',seq_along(seq))
df2 = df2 %>% mutate_if(is.factor,as.character)

df2 = df2 %>% 
  mutate(seq_string = Reduce(paste0,df2 %>% select(grep('event',names(df2))) %>% as.list)) %>% 
  filter(seq_string == paste0(seq,collapse=''))

time_diff = df2 %>% select(grep('time',names(df2))) %>%
  t %>%
  as.data.frame() %>%
  lapply(diff) %>% 
  unlist %>%  matrix(ncol=2,byrow=TRUE) %>% 
  as.data.frame

foreach(i=seq_along(time_diff),.combine=data.frame) %do%
{
  time_diff[[i]] %>% between(time_LB[i+1],time_UB[i+1])
} %>% 
  Reduce(`&`,.) %>% 
  which %>% 
  slice(df2,.) %>% 
  filter(time1 %>% between(time_LB[1],time_UB[1])) %>% # deal with time1 bounds, which we skipped over earlier
  group_by(id,time1) %>%
  slice(1) # slice 1 row for each unique id and time1 (so no duplicate time1s)

Это выводит следующее:

Source: local data frame [4 x 8]
Groups: id, time1 [4]

     id event1 time1 event2 time2 event3 time3 seq_string
  <dbl>  <chr> <dbl>  <chr> <dbl>  <chr> <dbl>      <chr>
1     1      a     1      b     6      a    24        aba
2     1      a    24      b    30      a    42        aba
3     2      a     1      b     6      a    24        aba
4     2      a     2      b     6      a    24        aba

Если вам нужны только подсчеты, вы можете group_by(id), затем count(), как в исходном фрагменте кода.

20.01.2017
  • Да, это опечатка ... должно быть 42. Спасибо, что исправили. Можно ли изменить ваш код для работы с последовательностями любой длины? Фактические последовательности содержат от 2 до 10 событий. 21.01.2017
  • Да, я отредактировал ответ выше, чтобы включить обобщенную версию. 21.01.2017
  • @ dmitriy873 Пожалуйста, дайте мне знать, если это не сработает должным образом или мне нужно добавить дополнительные пояснения к самому коду. Спасибо. 21.01.2017

  • 2

    Возможно, проще представить последовательности событий в виде строк и использовать регулярное выражение:

    df.str = lapply(split(df, df$id), function(d) {
        z = rep('-', tail(d,1)$time); z[d$time] = as.character(d$event); z })
    
    df.str = lapply(df.str, paste, collapse='')
    
    # > df.str
    # $`1`
    # [1] "a-b--b-----b-----------a-----b-----------a"
    #
    # $`2`
    # [1] "aa---b----------a------a"
    
    
    df1 = lapply(df.str, function(s) length(gregexpr('(?=a.{1,7}b.{11,17}a)', s, perl=T)[[1]]))
    
    > data.frame(id=names(df1), count=unlist(df1))
    #   id count
    # 1  1     2
    # 2  2     2
    
    20.01.2017
  • Это очень хорошая идея! Однако между реальным событием могут быть миллионы секунд. Увеличит ли это время выполнения? 21.01.2017
  • @ dmitriy873 Я понимаю, что оба ответа здесь работают, но мне действительно не нравится ни один из них, потому что они больше похожи на обходные пути, а не на прямое столкновение с проблемой. В каком-то смысле мы говорим здесь об обнаружении сигнала. Итак, убедитесь, что они работают на надуманном примере, но что делать, если последовательность суперсложная или среда, в которой вы ищете, очень шумная (много ложных возможных вариантов, которые следует опустить)? Эти ответы не будут хорошо масштабироваться. 21.01.2017
  • Хотите поделиться лучшим решением? 21.01.2017
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..