Как создать столбец последовательности на основе начала и конца последовательностей - PullRequest
0 голосов
/ 08 сентября 2018

У меня есть два столбца, которые содержат информацию о начале и конце последовательности. Из этого я хочу создать столбец последовательности, то есть каждая последовательность начинается, когда seq_start равен 1, и заканчивается первой строкой, которая появляется после seq_start = 1, в которой seq_end = 1. Как я могу сделать это с tidyverse? Данные показаны ниже, где seq - ожидаемый результат. Обратите внимание, что когда seq_end = 1 и seq_start = 1 в одних и тех же строках, получается последовательность длиной один.

structure(list(seq_start = c(NA, NA, NA, NA, NA, 1, NA, NA, NA, 
NA, NA, 1, NA, 1, NA, NA, NA, NA, NA, NA, 1, 1, NA, NA, NA, NA, 
NA, 1, 1, NA, NA, 1, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA, NA, NA, 1, 
NA), seq_end = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1L, 
1L, 1L, 1L, NA, NA, 1L, 1L, 1L, NA, 1L, NA, NA, NA, NA, NA, 1L, 
1L, NA, NA, 1L, 1L, NA, 1L, 1L, 1L, 1L, NA, NA, NA, 1L, 1L, NA, 
NA, NA, NA, NA, NA, 1L, NA, 1L, 1L, NA, 1L, 1L, NA, NA, 1L, 1L, 
1L), seq = c(NA, NA, NA, NA, NA, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 
NA, 3L, NA, NA, NA, NA, NA, NA, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 6L, 
7L, 7L, 7L, 8L, NA, NA, NA, 9L, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, 10L, 10L, NA, NA, NA, NA, NA, NA, NA, 11L, 
NA)), .Names = c("seq_start", "seq_end", "seq"), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -60L))

1 Ответ

0 голосов
/ 08 сентября 2018

Вот решение, которое интенсивно использует функцию lag() пакета dplyr вместе с cumsum() из пакета base для получения ожидаемого результата. Возможно, это не самое краткое решение, но я думаю, что оно достаточно интуитивно понятно:

d <- d %>%

  # new.seq.starts starts from 0, and increments by 1 every time seq_starts takes on 
  # the value 1, like this: 0, 0, 0, 1, 1, 1, 1, 2, 2, ...
  # Rows with the same new.seq.starts value are thus part of the same "run".
  mutate(new.seq.starts = cumsum(!is.na(seq_start))) %>%

  # group by each "run"
  group_by(new.seq.starts) %>%

  # any.ending.so.far counts whether there has been ANY seq_end == 1 within the run yet.
  # first.ending is TRUE only if it's the first row (within the run) to have an ending.
  mutate(any.ending.so.far = cumsum(!is.na(seq_end)),
         first.ending = any.ending.so.far == 1 &
           (is.na(lag(any.ending.so.far)) | lag(any.ending.so.far) < 1)) %>%
  ungroup() %>%

  # result keeps the new.seq.starts values only if there's no ending yet (i.e. 
  # any.ending.so.far == 0), or only just ended (first.ending == TRUE). Otherwise,
  # it takes on the value NA.
  mutate(result = ifelse(new.seq.starts > 0 &
                           (any.ending.so.far == 0 | first.ending),
                         new.seq.starts, NA)) %>%

  # Remove helper variables as they are no longer needed.
  select(-c(new.seq.starts, any.ending.so.far, first.ending))

> all.equal(d$seq, d$result)
[1] TRUE
...