【问题标题】:Filter Rows Between with Multiple Events per Subject过滤每个主题的多个事件之间的行
【发布时间】:2020-05-12 09:17:13
【问题描述】:

我有一个大型数据集,我正在尝试过滤每个主题的特定事件之后的天数。这个问题是,感兴趣的“事件”可能对某些受试者发生多次,而对于少数受试者,该事件根本不会发生(在这种情况下,它们可以从汇总数据中删除)。

以下是数据示例和我尝试过的示例:

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 行。由于主题内的多个事件,这两种方法都会产生错误。我想不出一个好的解决方法。

方法一:

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

方法二:

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))

【问题讨论】:

  • 是的,对不起,本。所需的输出将是一个数据框,其中每个主题的行对应于具有“事件”的行,然后是该事件之后的 2 行(存在事件时总共 3 行)。
  • 非常感谢使用 set.seed() 创建可重现的数据并分享您的编码尝试。一个准备充分的问题。

标签: r filter tidyverse


【解决方案1】:

如果要在event 中获取带有1 的行以及以下两行,可以执行以下操作。感谢 splitstackshape 包的作者 Ananda Mahto,我们可以使用返回列表的 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

【讨论】:

    【解决方案2】:

    这是一个基本的 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)
    

    【讨论】:

      【解决方案3】:

      这是一个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
      

      这符合我对这种极端情况的期望。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-14
        • 2020-07-15
        • 2020-06-22
        • 1970-01-01
        • 1970-01-01
        • 2020-06-23
        • 1970-01-01
        • 2020-08-06
        相关资源
        最近更新 更多