Соотношение значений по условию в R - PullRequest
3 голосов
/ 20 июня 2019

У меня есть фрейм необработанных данных, который выглядит следующим образом:

test
   id class                time
1   1 start 2019-06-20 00:00:00
2   1   end 2019-06-20 00:05:00
3   1 start 2019-06-20 00:10:00
4   1   end 2019-06-20 00:15:00
5   2   end 2019-06-20 00:20:00
6   2 start 2019-06-20 00:25:00
7   2   end 2019-06-20 00:30:00
8   2 start 2019-06-20 00:35:00
9   3   end 2019-06-20 00:40:00
10  3 start 2019-06-20 00:45:00
11  3   end 2019-06-20 00:50:00
12  3 start 2019-06-20 00:55:00

Моя цель - отобразить значения в выходную таблицу для каждого идентификатора только , где есть start и end в последовательном порядке (время). Поэтому вывод будет выглядеть так:

output
  id               start                 end
1  1 2019-06-20 00:00:00 2019-06-20 00:05:00
2  1 2019-06-20 00:10:00 2019-06-20 00:15:00
3  2 2019-06-20 00:25:00 2019-06-20 00:30:00
4  3 2019-06-20 00:45:00 2019-06-20 00:50:00

Я пробовал с пакетом dplyr, но

test %>% group_by(id) %>% arrange(time) %>% starts_with("start")
Error in starts_with(., "start") : is_string(match) is not TRUE

starts_with всегда выдает ошибку. Я хотел бы избежать написания цикла for, потому что уверен, что это может быть выполнено несколькими цепными операциями. Любые идеи для обхода в dplyr или data.table?

Ответы [ 4 ]

4 голосов
/ 20 июня 2019

Один из возможных подходов:

test[, {
        si <- which(class=="start" & shift(class, -1L)=="end")
        .(id, start=time[si], end=time[si + 1L])
    }, by=.(id)]

выход:

   id                 start                 end
1:  1 1 2019-06-20 00:00:00 2019-06-20 00:05:00
2:  1 1 2019-06-20 00:10:00 2019-06-20 00:15:00
3:  2 2 2019-06-20 00:25:00 2019-06-20 00:30:00
4:  3 3 2019-06-20 00:45:00 2019-06-20 00:50:00

данные:

library(data.table)
test <- fread("id,class,time
1,start,2019-06-20 00:00:00
1,end,2019-06-20 00:05:00
1,start,2019-06-20 00:10:00
1,end,2019-06-20 00:15:00
2,end,2019-06-20 00:20:00
2,start,2019-06-20 00:25:00
2,end,2019-06-20 00:30:00
2,start,2019-06-20 00:35:00
3,end,2019-06-20 00:40:00
3,start,2019-06-20 00:45:00
3,end,2019-06-20 00:50:00
3,start,2019-06-20 00:55:00")
3 голосов
/ 20 июня 2019

Я обычно использую cumsum () в этих случаях

test %>% 
  group_by(id) %>%
  arrange(time, .by_group = TRUE) %>%   # should use .by_group arg
  mutate(flag = cumsum(class == "start")) %>%
  group_by(id, flag) %>%
  filter(n() == 2L) %>%
  ungroup() %>%
  spread(class, time) %>%
  select(-flag)
2 голосов
/ 20 июня 2019

Вы можете оставить каждую start строку плюс end сразу после нее (если есть), затем используйте dcast для переключения с длинной на широкую форму:

test[, 
  if (.N >= 2) head(.SD, 2)
, by=.(g = rleid(id, cumsum(class=="start"))), .SDcols=names(test)][, 
  dcast(.SD, id + g ~ factor(class, levels=c("start", "end")), value.var="time")
]

   id g               start                 end
1:  1 1 2019-06-20 00:00:00 2019-06-20 00:05:00
2:  1 2 2019-06-20 00:10:00 2019-06-20 00:15:00
3:  2 4 2019-06-20 00:25:00 2019-06-20 00:30:00
4:  3 7 2019-06-20 00:45:00 2019-06-20 00:50:00

rleid и cumsum используются для поиска последовательностей; и factor необходимо, чтобы указать dcast порядок столбцов.

Примечание : По сути, это то же самое, что и ответ @ cheetahfly (я не осознавал, когда писал): так как совокупное увеличение увеличивается, достаточно сгруппировать по id + cumsum, и нет необходимо использовать rleid (для отслеживания прогонов значений). Единственное отличие состоит в том, что мой подход будет таким же, как начало, конец, конец; в то время как другой ответ отфильтровывает его с помощью проверки n () == 2.

2 голосов
/ 20 июня 2019

Используя dplyr и tidyr, мы можем сначала filter строки, которые следуют шаблону "start" и "end", создать группы из 2 строк и spread для длинного формата.

library(dplyr)
library(tidyr)

test %>%
  group_by(id) %>%
  filter(class == "start" & lead(class) == "end" | 
         class == "end" & lag(class) == "start") %>%
  group_by(group = gl(n()/2, 2)) %>%
  spread(class, time) %>%
  ungroup() %>%
  select(-group) %>%
  select(id, start, end)

#     id  start              end               
#   <int> <dttm>              <dttm>             
#1     1 2019-06-20 00:00:00 2019-06-20 00:05:00
#2     1 2019-06-20 00:10:00 2019-06-20 00:15:00
#3     2 2019-06-20 00:25:00 2019-06-20 00:30:00
#4     3 2019-06-20 00:45:00 2019-06-20 00:50:00
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...