Извлечение строк вокруг узоров в R - PullRequest
0 голосов
/ 20 февраля 2019

У меня есть data.frame test, где я бы хотел определить, что будет до и после шаблона bar - foo для каждого id.Шаблон должен быть последовательным по timestamp

Например, в следующем примере есть три паттерна bar - foo.

> test
             timestamp id message   result
1  2019-01-01 00:00:21  1     bar negative
2  2019-01-01 00:00:58  1     bar positive
3  2019-01-01 00:01:35  1     foo positive
4  2019-01-01 00:03:02  1     bar negative
5  2019-01-01 00:06:42  1     baz positive
6  2019-01-01 00:07:16  1     baz positive
7  2019-01-01 00:07:39  1     bar positive
8  2019-01-01 00:09:14  2     bar negative
9  2019-01-01 00:09:56  2     foo negative
10 2019-01-01 00:10:56  2     foo positive
11 2019-01-01 00:11:13  2     foo negative
12 2019-01-01 00:11:32  2     foo positive
13 2019-01-01 00:11:49  2     bar negative
14 2019-01-01 00:12:18  2     foo positive
15 2019-01-01 00:15:28  2     bar positive

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

> output
    before    after id
1 negative negative  1
2     <NA> positive  2
3 positive positive  2

Код, который я применил ниже , работает , но кажется сложным и неэффективным

test %>%
            group_by(id) %>%
            mutate(next.message = lead(message, order_by=timestamp),
                   previous.result = lag(result, order_by=timestamp),
                   next.result = lead(result, n = 2, order_by=timestamp)) %>%
            filter(message == 'bar', next.message == 'foo')  %>%
            filter_all(any_vars(!is.na(.))) %>% 
            select (-c(timestamp, message, result, next.message)) %>%
            rename(before = previous.result , after = next.result) 

Что может быть лучше для решения этой проблемы с использованием функций dplyr или data.table?

пример данных:

dput(test)
structure(list(timestamp = structure(c(1546318821, 1546318858, 
1546318895, 1546318982, 1546319202, 1546319236, 1546319259, 1546319354, 
1546319396, 1546319456, 1546319473, 1546319492, 1546319509, 1546319538, 
1546319728), class = c("POSIXct", "POSIXt")), id = c(1, 1, 1, 
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2), message = c("bar", "bar", 
"foo", "bar", "baz", "baz", "bar", "bar", "foo", "foo", "foo", 
"foo", "bar", "foo", "bar"), result = c("negative", "positive", 
"positive", "negative", "positive", "positive", "positive", "negative", 
"negative", "positive", "negative", "positive", "negative", "positive", 
"positive")), row.names = c(NA, -15L), class = "data.frame")

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

вот мой путь с data.table

test_dt <- setDT(test)
# add the before and after to all rows
test_dt[order(timestamp),
        c("before", "after") := list(shift(result, 1, type = 'lag'), 
                                     shift(result, 2, type = 'lead')), 
        by=id]

# filter the rows and select the columns you need
test_dt[message == 'bar' & shift(message, 1, type = 'lead') == 'foo', 
        list(before, after), by = id]
0 голосов
/ 20 февраля 2019

Может быть что-то вроде этого в data.table:

library(data.table)
setDT(test)
test[, 
    {
        #find the rows where message is bar and next message is foo
        v <- .I[message=="bar" & shift(message, -1L, fill="")=="foo"]

          #extract the previous result and use NA if its beyond the starting row index of current id
        .(before=test[replace(v - 1L, v - 1L < min(.I), NA_integer_), result],

            #extract the next result and use NA if its beyond the ending row index of current id
            after=test[replace(v + 2L, v + 2L > max(.I), NA_integer_), result])
    },
    by=.(id)]

вывод:

   id   before    after
1:  1 negative negative
2:  2     <NA> positive
3:  2 positive positive
...