【问题标题】:Comparing dates in different columns to isolate certain within-group entries in R比较不同列中的日期以隔离 R 中的某些组内条目
【发布时间】:2020-12-03 17:52:13
【问题描述】:

我有一个包含重复项的 ID 列的数据框。有一个名为 type 的列,它采用值“S”或“N”。还有两个额外的日期列 - 入院日期和出院日期。我的问题有点类似于comparing two data frames and isolating rows based on certain date differences,但不完全一样。如果需要,我可以将我的数据分成两个数据框,但我想知道是否可以在没有额外步骤的情况下完成我想要的。

这是两个患者的数据在 R 中的样子的一个小例子:

example <- data.frame(ID = c(22,22,22,52,52,52), 
admission_date = c("2013-10-03","2014-03-11","2014-03-16","2012-02-08","2014-06-10","2014-06-20"),
discharge_date = c("2013-10-11","2014-03-16","2014-03-28","2012-02-13","2014-06-12","2014-06-30"), 
type = c('S','S','N','S','S','N'))

我想做的是在患者中比较类型变量中取值为“N”的条目和取值为“S”的条目。根据值为“S”的条目的出院日期,我想找到值“N”的条目,其入院日期在前者出院日期的 5 天内(值为“S”的出院日期应该是值“N”的录取日期之前)。

因此,在示例数据框中,应该保留的唯一两个条目是第 2 行和第 3 行,而不是第 5 和 6 行,因为入院日期和出院日期之间的差异大于 5。

有人对我如何过滤这些数据有任何建议吗?非常感谢任何帮助。

【问题讨论】:

    标签: r date filter dplyr


    【解决方案1】:

    这是一个有趣的挑战。原因之一是迭代行不如迭代列直观(请参阅此问题以获取大量建议:For each row in an R dataframe)。

    现在我知道矢量化解决方案比 for 循环更受欢迎,但这个问题的挑战之一是,我们不只是在每一行上执行函数,而是将迭代的行与其他行进行比较,并在执行过程中删除一些行沿着。我希望有更好的解决方案,我希望有人发布更好的解决方案来帮助我学习。

    在我开始之前有个小提示,“示例”不是一个很好的对象名称,因为它也是基础 R 中的一个函数。此外,如果我们只处理“S”的交替行,解决方案会容易得多" 和 "N" - 也就是说,如果许多 S 在 N 之前,那么只有最低的 S 可能在 N 的 5 天内。尽管如此,努力解决更具挑战性的情况是值得的。

    最终我将这个问题解决为一个两阶段的问题,每个阶段都用一个 for 循环解决。首先,我取出了所有不在对应 N 行的 5 天内的 S 行。然后我取出那些没有任何合适的S同伴的N行。所有这些都在基础 R 中实现。

    开始吧:

        example_df <- data.frame(ID = c(22,22,22,52,52,52), 
                      admission_date = c("2013-10-03","2014-03-11","2014-03-16","2012-02-08","2014-06-10","2014-06-20"),
                      discharge_date = c("2013-10-11","2014-03-16","2014-03-28","2012-02-13","2014-06-12","2014-06-30"), 
                      type = c('S','S','N','S','S','N'))
    
        example_df$admission_date<-as.numeric(as.Date(example_df$admission_date))
        example_df$discharge_date<-as.numeric(as.Date(example_df$discharge_date))
    

    我做的第一件事是获取日期列(它们是字符)并根据日期将它们转换为数字。最初我正在对日期对象进行数学运算,但由于我最终使用的子集运算变得复杂。

    这是第一个 for 循环:

       del_vec <- vector("integer")
       for( i in 1:nrow(example_df))  {
       if (example_df[i,"type"]== "S") {
       next
       }
       if (example_df[i,"type"] == "N") {
       add_on <- which
       (
       example_df["type"] == "S" & 
        example_df["ID"]==example_df[i,"ID"] &
         example_df["discharge_date"] < (example_df[i,"admission_date"] - 5)
         )
    }
    
          del_vec<- append(del_vec,add_on)
     } 
       example_df_new <- example_df[-c(del_vec),]
       rownames(example_df_new) <- 1:nrow(example_df_new)
      example_df_new
    

    我在这里所做的是首先创建一个向量,该向量将包含我们删除的行号。为了摆脱不合适的 S 行,我们需要实际处理 N 行,所以我让循环跳过 S 行。然后当循环遇到N行时,我们找到满足以下条件的行:

    1. 有S型
    2. 与有问题的 N 行具有相同的 ID
    3. 出院日期距相关 N 行的入院日期超过 5 天

    使用which()捕获满足这些条件的行号。现在我将这些行添加到空向量中,并将它们从原始 df 中删除。我还重命名了新 df 的行以获得example_df_new 的以下输出

       ID    admission_date discharge_date type
     1 22          16140          16145    S
     2 22          16145          16157    N
     3 52          16241          16251    N
    

    所以我们保留了您想要保留的 2 行,但现在我们想要删除底部的这行。我在第二个循环中执行此操作,该循环遍历新减少的 df 中的行:

        del_vec2 <- vector()
        for(i in 1:nrow(example_df_new)) {
        if (example_df_new[i,"type"]=="S") {
        next
         }
        if (example_df_new[i,"type"] == "N") {
        add_on_two <- which(example_df_new["type"] == "S" &    example_df_new["ID"] == example_df_new[i,"ID"])
        }
        if(length(add_on_two !=0)) {
        next
        } else {
        del_vec2 <- append(del_vec2,i)
         }
        }
    
        example_df_3<-example_df_new[-c(del_vec2),]
        example_df_3
    

    再一次,我们告诉循环跳过 S 行——无论哪个第一次剪切都应该留在里面。现在,当循环遇到 N 行时,我们要求循环寻找满足以下条件的行:

    1. 是S型
    2. 与有问题的 N 行具有相同的 ID

    我再次使用which() 来保存这些行的位置。如果满足这些标准,那么我们跳过 - 我们希望保留所有具有适当 S 同伴的 N。如果不是,那么我们将 (i) 的行号添加到我们要删除的行向量中。

    然后我们删除这些行并最终得到所需的输出:

         ID     admission_date discharge_date type
       1 22          16140          16145       S
       2 22          16145          16157       N
    

    此时您可以将日期列更改回日期格式。 同样,虽然这可能是第一个,但我认为这不是最好的解决方案。我希望看到改进的解决方案,但问题比最初看起来更棘手。

    【讨论】:

    • 您好,非常感谢您提出的全面建议。一个快速的问题 - 在运行第一个 for 循环并删除不符合条件的行时,我被抛出一个“一元运算符的参数无效”错误。更具体地说,在这一行:example_df_new &lt;- example_df[-c(del_vec), ] 我认为这与 del_vec 的子集有关,但我不太确定。
    • 经过进一步检查,我认为问题在于 for 循环而不是子集,因为 del_vec(当我运行它时)是一个长度为 2 的列表,由函数组成,而不是由应排除。我会玩弄它。再次感谢您!
    【解决方案2】:

    尝试在同一数据框中进行过滤后,我决定将数据分成两个表:一个只包含“S”类型的数据,另一个只包含“N”类型的数据。然后,我在匹配 ID 列时进行了完全连接。虽然这比以前创建了更多的行,但我能够比较两个感兴趣的日期。结果数据框仅包含一行 - 入院日期为“N”的患者在“S”类型的出院日期后 5 天内输入。

    R中的代码如下:

    library(dplyr)
    
    example_df <- data.frame(ID = c(22,22,22,52,52,52), 
                             admission_date = c("2013-10-03","2014-03-11","2014-03-16","2012-02-08","2014-06-10","2014-06-20"),
                             discharge_date = c("2013-10-11","2014-03-16","2014-03-28","2012-02-13","2014-06-12","2014-06-30"), 
                             type = c('S','S','N','S','S','N'))
    N_only <- example_df %>%
      filter(type == "N")
    S_only <- example_df %>%
      filter(type == "S")
    
    example_df_merged <- merge(N_only, S_only, by = "ID")
    example_df_merged$admission_date.x <- as.Date(as.character(example_df_merged$admission_date.x), format="%Y-%m-%d")
    example_df_merged$discharge_date.y <- as.Date(as.character(example_df_merged$discharge_date.y), format="%Y-%m-%d")
    example_df_merged$dateDiff <- example_df_merged$discharge_date.y - example_df_merged$admission_date.x
    
    example_df_final <- example_df_merged %>%
      filter(dateDiff <= 5 & dateDiff >= 0)
    

    为了更清晰的变量名称,我会更改以“.x”和“.y”结尾的变量,但这不是必需的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多