R中的迭代过滤
Iterative filtering in R
我有一个诊所就诊病人的数据集。每个病人可以多次访问。每个患者由 study_id 标识,每次就诊由 illness_id 标识。我想迭代过滤数据框,以便删除上次访问后 28 天内发生的访问。
我不能简单地计算所有访问之间的间隔,然后删除 28 天内发生的那些。过滤数据帧时需要迭代计算间隔。
在下面的示例中,您可以看到出现了 3 次患者 0003。始终保留访问 1。应删除访问 2,因为它发生在访问 1 后 7 天。一旦删除访问 2,访问 3 将在访问 1 后 29 天发生,因此应保留。但是,如果我计算所有间隔,然后过滤掉间隔为 28 天或更短的任何访问,则访问 2 和访问 3 都将被删除(因为访问 2 发生在访问 1 后 7 天,而访问 3 发生在访问 2 后 22 天) .
study_id
illness_id
illness_date
0001
000103/12/2007
2007/12/03
0002
000224/03/2008
2008/03/24
0002
000226/04/2008
2008/04/26
0002
000217/07/2008
2008/07/17
0002
000221/08/2008
2008/08/21
0002
000225/08/2008
2008/08/25
0003
000329/09/2008
2008/09/29
0003
000306/10/2008
2008/10/06
0003
000328/10/2008
2008/10/28
正确过滤的数据框应该是:
study_id
illness_id
illness_date
0001
000103/12/2007
2007/12/03
0002
000224/03/2008
2008/03/24
0002
000226/04/2008
2008/04/26
0002
000217/07/2008
2008/07/17
0002
000221/08/2008
2008/08/21
0003
000329/09/2008
2008/09/29
0003
000328/10/2008
2008/10/28
感谢您的帮助 - 我是 R 的新手,正在努力了解迭代和循环。如果有一个涉及 dplyr 过滤器的简单解决方案那就太好了。
为了回应下面的一些建议,我将发布另一个示例来尝试让问题更清楚
'comparator' 行无法在每位患者的首次就诊时固定。它需要在数据帧中滚动,因为过滤是迭代完成的。对不起,如果这在 OP 中不清楚。这是一个示例,其中第 2、3 和 5 行应被删除,而第 1、4 和 6 行应保留。
第 2 行比第 1 行晚 8 天,因此已删除。第 3 行比第 1 行晚 26 天,因此被删除。第 4 行是第 1 行之后的 41 天,因此保留并成为该患者后续就诊的比较对象。第 5 行在第 4 行之后 6 天,因此被删除。第 6 行在第 4 行之后 31 天,因此被保留并成为该患者后续就诊的比较对象。
study_id
illness_id
illness_date
0001
000119/12/2007
19/12/2007
0001
000127/12/2007
27/12/2007
0001
000114/01/2008
14/01/2008
0001
000129/01/2008
29/01/2008
0001
000104/02/2008
04/02/2008
0001
000129/02/2008
29/02/2008
这里是@sbarbit提供的v优雅解决方案——诚挚感谢!!
df1 <- df |>
arrange(study_id, illness_date) |>
mutate(comparator = purrr::accumulate(illness_date,~ifelse(.y - .x > 28, .y,.x))) |>
mutate(daydiff = illness_date - lag(comparator, 1,0)) |>
mutate(daydiff = as.numeric(daydiff)) |>
filter(daydiff > 28)
这应该可以解决问题:
df %>%
mutate(illness_date = as.Date(illness_date,
format = "%Y/%m/%d")) %>%
group_by(study_id) %>%
mutate(time_since_first_visit = illness_date - min(illness_date)) %>%
filter(time_since_first_visit == 0 | time_since_first_visit > 28)
这是一个 returns 要删除的行的函数,以及使用 data.table
按组调用它的示例。
fFilter <- function(v, gap) {
blnDrop <- logical(length(v))
if (length(v) > 1L) {
prev <- v[1]
for (i in 2:length(v)) {
if (v[i] - prev <= gap) blnDrop[i] <- TRUE else prev <- v[i]
}
}
blnDrop
}
library(data.table)
dt <- data.table(id = rep(1:3, c(1, 5, 3)), date = as.Date(c("2007/12/3", "2008/3/24", "2008/4/26", "2008/7/17", "2008/8/21", "2008/8/25", "2008/9/29", "2008/10/6", "2008/10/28")))
setorder(dt, id, date)
dt[,drop := fFilter(date, 28), by = "id"][drop == FALSE, 1:(length(dt) - 1L)]
#> id date
#> 1: 1 2007-12-03
#> 2: 2 2008-03-24
#> 3: 2 2008-04-26
#> 4: 2 2008-07-17
#> 5: 2 2008-08-21
#> 6: 3 2008-09-29
#> 7: 3 2008-10-28
这里我使用purrr::accumulate
只传播距离前一个日期超过28天的日期,否则保留前一个日期。然后,根据与更新的前一行的比较过滤行。
数据:
df <- data.frame(illness_id = c("000103/12/2007",
"000224/03/2008",
"000226/04/2008",
"000217/07/2008",
"000221/08/2008",
"000225/08/2008",
"000329/09/2008",
"000306/10/2008",
"000328/10/2008"),
illness_date = as.Date(c("2007/12/03",
"2008/03/24",
"2008/04/26",
"2008/07/17",
"2008/08/21",
"2008/08/25",
"2008/09/29",
"2008/10/06",
"2008/10/28"),
format = "%Y/%m/%d"),
study_id = c("0001",
"0002",
"0002",
"0002",
"0002",
"0002",
"0003",
"0003",
"0003"))
这里我展示了没有过滤步骤的算法来说明它是如何工作的:
library(dplyr)
library(purrr)
df |>
group_by(study_id) |>
arrange(illness_date, by_group = TRUE) |>
mutate(comparator = purrr::accumulate(illness_date,~ifelse(.y - .x > 28, .y,.x))) |>
mutate(daydiff = illness_date - lag(comparator, 1,0))
+ # A tibble: 9 x 5
# Groups: study_id [3]
illness_id illness_date study_id comparator daydiff
<chr> <date> <chr> <dbl> <dbl>
1 000103/12/2007 2007-12-03 0001 13850 13850
2 000224/03/2008 2008-03-24 0002 13962 13962
3 000226/04/2008 2008-04-26 0002 13995 33
4 000217/07/2008 2008-07-17 0002 14077 82
5 000221/08/2008 2008-08-21 0002 14112 35
6 000225/08/2008 2008-08-25 0002 14112 4
7 000329/09/2008 2008-09-29 0003 14151 14151
8 000306/10/2008 2008-10-06 0003 14151 7
9 000328/10/2008 2008-10-28 0003 14180 29
这里我添加过滤步骤:
df |>
group_by(study_id) |>
arrange(illness_date, by_group = TRUE) |>
mutate(comparator = purrr::accumulate(illness_date,~ifelse(.y - .x > 28, .y,.x))) |>
mutate(daydiff = illness_date - lag(comparator, 1,0)) |>
filter(daydiff > 28)
# A tibble: 7 x 5
# Groups: study_id [3]
illness_id illness_date study_id comparator daydiff
<chr> <date> <chr> <dbl> <dbl>
1 000103/12/2007 2007-12-03 0001 13850 13850
2 000224/03/2008 2008-03-24 0002 13962 13962
3 000226/04/2008 2008-04-26 0002 13995 33
4 000217/07/2008 2008-07-17 0002 14077 82
5 000221/08/2008 2008-08-21 0002 14112 35
6 000329/09/2008 2008-09-29 0003 14151 14151
7 000328/10/2008 2008-10-28 0003 14180 29
我有一个诊所就诊病人的数据集。每个病人可以多次访问。每个患者由 study_id 标识,每次就诊由 illness_id 标识。我想迭代过滤数据框,以便删除上次访问后 28 天内发生的访问。
我不能简单地计算所有访问之间的间隔,然后删除 28 天内发生的那些。过滤数据帧时需要迭代计算间隔。
在下面的示例中,您可以看到出现了 3 次患者 0003。始终保留访问 1。应删除访问 2,因为它发生在访问 1 后 7 天。一旦删除访问 2,访问 3 将在访问 1 后 29 天发生,因此应保留。但是,如果我计算所有间隔,然后过滤掉间隔为 28 天或更短的任何访问,则访问 2 和访问 3 都将被删除(因为访问 2 发生在访问 1 后 7 天,而访问 3 发生在访问 2 后 22 天) .
study_id | illness_id | illness_date |
---|---|---|
0001 | 000103/12/2007 | 2007/12/03 |
0002 | 000224/03/2008 | 2008/03/24 |
0002 | 000226/04/2008 | 2008/04/26 |
0002 | 000217/07/2008 | 2008/07/17 |
0002 | 000221/08/2008 | 2008/08/21 |
0002 | 000225/08/2008 | 2008/08/25 |
0003 | 000329/09/2008 | 2008/09/29 |
0003 | 000306/10/2008 | 2008/10/06 |
0003 | 000328/10/2008 | 2008/10/28 |
正确过滤的数据框应该是:
study_id | illness_id | illness_date |
---|---|---|
0001 | 000103/12/2007 | 2007/12/03 |
0002 | 000224/03/2008 | 2008/03/24 |
0002 | 000226/04/2008 | 2008/04/26 |
0002 | 000217/07/2008 | 2008/07/17 |
0002 | 000221/08/2008 | 2008/08/21 |
0003 | 000329/09/2008 | 2008/09/29 |
0003 | 000328/10/2008 | 2008/10/28 |
感谢您的帮助 - 我是 R 的新手,正在努力了解迭代和循环。如果有一个涉及 dplyr 过滤器的简单解决方案那就太好了。
为了回应下面的一些建议,我将发布另一个示例来尝试让问题更清楚
'comparator' 行无法在每位患者的首次就诊时固定。它需要在数据帧中滚动,因为过滤是迭代完成的。对不起,如果这在 OP 中不清楚。这是一个示例,其中第 2、3 和 5 行应被删除,而第 1、4 和 6 行应保留。
第 2 行比第 1 行晚 8 天,因此已删除。第 3 行比第 1 行晚 26 天,因此被删除。第 4 行是第 1 行之后的 41 天,因此保留并成为该患者后续就诊的比较对象。第 5 行在第 4 行之后 6 天,因此被删除。第 6 行在第 4 行之后 31 天,因此被保留并成为该患者后续就诊的比较对象。
study_id | illness_id | illness_date |
---|---|---|
0001 | 000119/12/2007 | 19/12/2007 |
0001 | 000127/12/2007 | 27/12/2007 |
0001 | 000114/01/2008 | 14/01/2008 |
0001 | 000129/01/2008 | 29/01/2008 |
0001 | 000104/02/2008 | 04/02/2008 |
0001 | 000129/02/2008 | 29/02/2008 |
这里是@sbarbit提供的v优雅解决方案——诚挚感谢!!
df1 <- df |>
arrange(study_id, illness_date) |>
mutate(comparator = purrr::accumulate(illness_date,~ifelse(.y - .x > 28, .y,.x))) |>
mutate(daydiff = illness_date - lag(comparator, 1,0)) |>
mutate(daydiff = as.numeric(daydiff)) |>
filter(daydiff > 28)
这应该可以解决问题:
df %>%
mutate(illness_date = as.Date(illness_date,
format = "%Y/%m/%d")) %>%
group_by(study_id) %>%
mutate(time_since_first_visit = illness_date - min(illness_date)) %>%
filter(time_since_first_visit == 0 | time_since_first_visit > 28)
这是一个 returns 要删除的行的函数,以及使用 data.table
按组调用它的示例。
fFilter <- function(v, gap) {
blnDrop <- logical(length(v))
if (length(v) > 1L) {
prev <- v[1]
for (i in 2:length(v)) {
if (v[i] - prev <= gap) blnDrop[i] <- TRUE else prev <- v[i]
}
}
blnDrop
}
library(data.table)
dt <- data.table(id = rep(1:3, c(1, 5, 3)), date = as.Date(c("2007/12/3", "2008/3/24", "2008/4/26", "2008/7/17", "2008/8/21", "2008/8/25", "2008/9/29", "2008/10/6", "2008/10/28")))
setorder(dt, id, date)
dt[,drop := fFilter(date, 28), by = "id"][drop == FALSE, 1:(length(dt) - 1L)]
#> id date
#> 1: 1 2007-12-03
#> 2: 2 2008-03-24
#> 3: 2 2008-04-26
#> 4: 2 2008-07-17
#> 5: 2 2008-08-21
#> 6: 3 2008-09-29
#> 7: 3 2008-10-28
这里我使用purrr::accumulate
只传播距离前一个日期超过28天的日期,否则保留前一个日期。然后,根据与更新的前一行的比较过滤行。
数据:
df <- data.frame(illness_id = c("000103/12/2007",
"000224/03/2008",
"000226/04/2008",
"000217/07/2008",
"000221/08/2008",
"000225/08/2008",
"000329/09/2008",
"000306/10/2008",
"000328/10/2008"),
illness_date = as.Date(c("2007/12/03",
"2008/03/24",
"2008/04/26",
"2008/07/17",
"2008/08/21",
"2008/08/25",
"2008/09/29",
"2008/10/06",
"2008/10/28"),
format = "%Y/%m/%d"),
study_id = c("0001",
"0002",
"0002",
"0002",
"0002",
"0002",
"0003",
"0003",
"0003"))
这里我展示了没有过滤步骤的算法来说明它是如何工作的:
library(dplyr)
library(purrr)
df |>
group_by(study_id) |>
arrange(illness_date, by_group = TRUE) |>
mutate(comparator = purrr::accumulate(illness_date,~ifelse(.y - .x > 28, .y,.x))) |>
mutate(daydiff = illness_date - lag(comparator, 1,0))
+ # A tibble: 9 x 5
# Groups: study_id [3]
illness_id illness_date study_id comparator daydiff
<chr> <date> <chr> <dbl> <dbl>
1 000103/12/2007 2007-12-03 0001 13850 13850
2 000224/03/2008 2008-03-24 0002 13962 13962
3 000226/04/2008 2008-04-26 0002 13995 33
4 000217/07/2008 2008-07-17 0002 14077 82
5 000221/08/2008 2008-08-21 0002 14112 35
6 000225/08/2008 2008-08-25 0002 14112 4
7 000329/09/2008 2008-09-29 0003 14151 14151
8 000306/10/2008 2008-10-06 0003 14151 7
9 000328/10/2008 2008-10-28 0003 14180 29
这里我添加过滤步骤:
df |>
group_by(study_id) |>
arrange(illness_date, by_group = TRUE) |>
mutate(comparator = purrr::accumulate(illness_date,~ifelse(.y - .x > 28, .y,.x))) |>
mutate(daydiff = illness_date - lag(comparator, 1,0)) |>
filter(daydiff > 28)
# A tibble: 7 x 5
# Groups: study_id [3]
illness_id illness_date study_id comparator daydiff
<chr> <date> <chr> <dbl> <dbl>
1 000103/12/2007 2007-12-03 0001 13850 13850
2 000224/03/2008 2008-03-24 0002 13962 13962
3 000226/04/2008 2008-04-26 0002 13995 33
4 000217/07/2008 2008-07-17 0002 14077 82
5 000221/08/2008 2008-08-21 0002 14112 35
6 000329/09/2008 2008-09-29 0003 14151 14151
7 000328/10/2008 2008-10-28 0003 14180 29