这是一个有趣的挑战。原因之一是迭代行不如迭代列直观(请参阅此问题以获取大量建议: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行时,我们找到满足以下条件的行:
- 有S型
- 与有问题的 N 行具有相同的 ID
- 出院日期距相关 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 行时,我们要求循环寻找满足以下条件的行:
- 是S型
- 与有问题的 N 行具有相同的 ID
我再次使用which() 来保存这些行的位置。如果满足这些标准,那么我们跳过 - 我们希望保留所有具有适当 S 同伴的 N。如果不是,那么我们将 (i) 的行号添加到我们要删除的行向量中。
然后我们删除这些行并最终得到所需的输出:
ID admission_date discharge_date type
1 22 16140 16145 S
2 22 16145 16157 N
此时您可以将日期列更改回日期格式。
同样,虽然这可能是第一个,但我认为这不是最好的解决方案。我希望看到改进的解决方案,但问题比最初看起来更棘手。