Фильтрация строк между несколькими событиями на тему - PullRequest
2 голосов
/ 27 января 2020

У меня большой набор данных, и я пытаюсь отфильтровать дни после определенного события c для каждого субъекта. Эта проблема заключается в том, что интересующее «событие» может происходить несколько раз для некоторых субъектов, а для некоторых субъектов событие вообще не происходит (в этом случае их можно просто удалить из обобщенных данных).

Вот пример данных и того, что я пробовал:

library(tidyverse)

set.seed(355)
subject <- c(rep(LETTERS[1:4], each = 40), rep("E", times = 40))
event <- c(sample(0:1, size = length(subject)-40, replace = T, prob = c(0.95, 0.05)), rep(0, times = 40))
df <- data.frame(subject, event)


df %>%
    filter(event == 1) %>%
    count(subject, event, sort = T)

# A tibble: 4 x 3
  subject event     n
  <fct>   <dbl> <int>
1 D           1     3
2 A           1     2
3 B           1     2
4 C           1     2

Итак, мы видим, что у субъекта D было событие 3 раза, в то время как у субъектов A, B и C это событие было 2 раза. Субъект E вообще не имел события.

Моим следующим шагом было создание тега «событие», который идентифицирует, где произошло каждое событие, а затем генерирует NA для всех строк. Я также создал последовательность событий, которая располагается между событиями, потому что я думал, что это может быть полезно, но я не пытался использовать ее.

df_cleaned <- df %>%
    group_by(subject, event) %>%
    mutate(event_seq = seq_along(event == 1),
        event_detail = ifelse(event == 1, "event", NA)) %>%
    as.data.frame() 

Я пробовал два разных подхода, используя filter() и between(), чтобы получить каждое событие и 2 строки после каждого события. Оба этих подхода создают ошибку из-за множества событий внутри субъекта. Я не могу найти хороший обходной путь для этого.

Подход 1:

df_cleaned %>%
    group_by(subject) %>%
    filter(., between(row_number(), 
        left = which(!is.na(event_detail)),
        right = which(!is.na(event_detail)) + 1))

Подход 2:

df_cleaned %>%
    group_by(subject) %>%
    mutate(event_group = cumsum(!is.na(event_detail))) %>%
    filter(., between(row_number(), left = which(event_detail == "event"), right = which(event_detail == "event") + 2))

Ответы [ 3 ]

1 голос
/ 27 января 2020

Вот базовая опция R, которая похожа на попытку @ jazzurro. Мы получаем индексы строк, где event == 1, затем выбираем следующие две строки из каждого индекса, используем unique, поэтому в случае перекрывающихся индексов мы выбираем только уникальные и подставляем их из исходного df.

inds <- which(df$event == 1)
df[unique(c(sapply(inds, `+`, 0:2))), ]

#    subject event
#3         A     1
#4         A     0
#5         A     0
#22        A     1
#23        A     0
#24        A     0
#59        B     1
#60        B     0
#61        B     0
#62        B     1
#63        B     0
#64        B     0
#....

Другим вариантом использования dplyr может быть lag

library(dplyr)
df %>%
  group_by(subject) %>%
  filter(event == 1 | lag(event) == 1 | lag(event, 2) == 1)
1 голос
/ 27 января 2020

Вот подход tidyverse, который использует cumsum() для создания групп строк после (и в том числе) события и который выбирает первые 3 строки каждой группы:

df %>%
  group_by(subject) %>%
  mutate(event_group = cumsum(event == 1L)) %>% 
  group_by(event_group, add = TRUE) %>% 
  filter(event_group > 0 & row_number() <= 3L)
# A tibble: 27 x 3
# Groups:   subject, event_group [9]
   subject event event_group
   <fct>   <dbl>       <int>
 1 A           1           1
 2 A           0           1
 3 A           0           1
 4 A           1           2
 5 A           0           2
 6 A           0           2
 7 B           1           1
 8 B           0           1
 9 B           0           1
10 B           1           2
# … with 17 more rows

Для тестирования граничного случая вот модифицированный набор данных, где subject A начинается с трех последующих событий. Кроме того, я добавил номера строк rn, чтобы проверить правильность выбора строк:

df2 <- df %>% 
  mutate(event = ifelse(row_number() <= 2L, 1L, event),
         rn = row_number())

Теперь мы получаем

df2 %>%
  group_by(subject) %>%
  mutate(event_group = cumsum(event == 1L)) %>% 
  group_by(event_group, add = TRUE) %>% 
  filter(event_group > 0 & row_number() <= 3L)
# A tibble: 29 x 4
# Groups:   subject, event_group [11]
   subject event    rn event_group
   <fct>   <dbl> <int>       <int>
 1 A           1     1           1
 2 A           1     2           2
 3 A           1     3           3
 4 A           0     4           3
 5 A           0     5           3
 6 A           1    22           4
 7 A           0    23           4
 8 A           0    24           4
 9 B           1    59           1
10 B           0    60           1
# … with 19 more rows

что соответствует моим ожиданиям в этом крайнем случае.

1 голос
/ 27 января 2020

Если вы хотите получить строки с 1 в event и следующих двух строках, вы можете сделать следующее. Благодаря Ananda Mahto, который является автором пакета splitstackshape, мы можем обработать этот тип операции с помощью getMyRows(), который возвращает список. Вы можете указать диапазон строк в функции. Здесь я сказал 0: 2. Поэтому я прошу R взять каждую строку с 1 в событии и следующие две строки. Я использовал bind_rows(), чтобы вернуть фрейм данных. Но если вам нужно работать со списком, вам не обязательно это делать.

install_github("mrdwab/SOfun")
library(SOfun)
library(dplyr)

ind <- which(x = df$event == 1)
bind_rows(getMyRows(data = df, pattern = ind, range = 0:2))

   subject event
1        A     1
2        A     0
3        A     0
4        A     1
5        A     0
6        A     0
7        B     1
8        B     0
9        B     0
10       B     1
11       B     0
12       B     0
13       C     1
14       C     0
15       C     0
16       C     1
17       C     0
18       C     0
19       D     1
20       D     0
21       D     0
22       D     1
23       D     0
24       D     0
25       D     1
26       D     0
27       D     0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...