R有条件地向前和向后结转

R carry forward and backward conditionally

我有一组变量,其中包含关于一个人是否 曾经 有某些健康状况的数据。例如,"have you ever had a heart attack?"

如果他们在观察 2 中说 "yes",那么在观察 3 和 4 中答案仍然是肯定的。但是,在观察 1 中不一定是肯定的。心脏病发作可能发生在观察 1 和观察之间2.

如果他们在观察 2 中说 "no",那么在观察 1 中答案是否定的。但是,在观察 3 或 4 中不一定是否。

这是一个可重现的例子:

df <- tibble(
  id = rep(1:3, each = 4),
  obs = rep(1:4, times = 3),
  mi_ever = c(NA, 0, 1, NA, NA, 0, NA, NA, NA, 1, NA, NA)
)
df
   id obs mi_ever
1   1   1      NA
2   1   2       0
3   1   3       1
4   1   4      NA
5   2   1      NA
6   2   2       0
7   2   3      NA
8   2   4      NA
9   3   1      NA
10  3   2       1
11  3   3      NA
12  3   4      NA

使用 zoo::na.locf 将我的 0(否)向后传送 将我的 1(是)向前传送是微不足道的。但是,我不确定如何将 0 向后 1 向前。理想情况下,我想要以下结果:

   id obs mi_ever mi_ever_2
1   1   1      NA         0
2   1   2       0         0
3   1   3       1         1
4   1   4      NA         1
5   2   1      NA         0
6   2   2       0         0
7   2   3      NA        NA
8   2   4      NA        NA
9   3   1      NA        NA
10  3   2       1         1
11  3   3      NA         1
12  3   4      NA         1

我查看了以下帖子,但 none 似乎完全涵盖了我在这里要问的内容。

making a "dropdown" function in R

感谢任何帮助。

基本上我是按顺序标记第一个 1 之后的项目成为 1,最后一个 0 之前的项目成为 0。

 ever <- function (x)  min( which( x == 1)) 
 NA_1 <- function(x) seq_along(x) > ever(x)  #could have done in one function
 # check to see if working
 ave(df$mi_ever, df$id, FUN= function(x){ x[NA_1(x) ] <- 1; x})
 [1] NA  0  1  1 NA  0 NA NA NA  1  1  1

 NA_0 <- function(x) seq_along(x) < not_yet(x)
 not_yet <- function(x){ max( which( x==0)) }
# make temporary version of 1-modified column
 temp1 <- ave(df$mi_ever, df$id, FUN= function(x){ x[NA_1(x) ] <- 1; x})
 df$ever2 <- ave(temp1, df$id, FUN= function(x){ x[NA_0(x) ] <- 0; x})
# then make final version; could have done it "in place" I suppose.
 df
# A tibble: 12 x 4
      id   obs mi_ever ever2
   <int> <int>   <dbl> <dbl>
 1     1     1      NA     0
 2     1     2       0     0
 3     1     3       1     1
 4     1     4      NA     1
 5     2     1      NA     0
 6     2     2       0     0
 7     2     3      NA    NA
 8     2     4      NA    NA
 9     3     1      NA    NA
10     3     2       1     1
11     3     3      NA     1
12     3     4      NA     1

如果你需要抑制应该可以的警告。

我从上面的@42- 那里得到了答案(谢谢!),并对其进行了一些调整以进一步满足我的需求。具体来说,我:

  • 注意警告 "no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf"。
  • 将单独的函数合并为一个函数(尽管单独的函数对学习非常有用)。
  • 添加了一个可选的 check_logic 参数。当 TRUE 时,如果 0 出现在 1 之后,函数将 return 9。这表示需要进一步调查的数据错误或逻辑缺陷。
  • 添加了一个将函数与 data.table 一起使用的示例,并且一次用于多个变量。这更准确地代表了我在现实生活中如何使用该功能,我认为它可能对其他人有用。

函数:

distribute_ever <- function(x, check_logic = TRUE, ...) {
  if (check_logic) {
    if (length(which(x == 1)) > 0 & length(which(x == 0)) > 0) {
      if (min(which(x == 1)) < max(which(x == 0))) {
        x <- 9                              # Set x to 9 if zero comes after 1
      }
    }
  }
  ones <- which(x == 1)                     # Get indices for 1's
  if (length(ones) > 0) {                   # Prevents warning
    first_1_by_group <- min(which(x == 1))  # Index first 1 by group
    x[seq_along(x) > first_1_by_group] <- 1 # Set x at subsequent indices to 1
  }
  zeros <- which(x == 0)                    # Get indices for 0's
  if (length(zeros) > 0) {                  # Prevents warning
    last_0_by_group <- max(which(x == 0))   # Index last 0 by group
    x[seq_along(x) < last_0_by_group] <- 0  # Set x at previous indices to 0
  }
  x
}

具有多个 "ever" 变量的新的可重现示例和一些在 1 之后有 0 的情况:

dt <- data.table(
  id = rep(1:3, each = 4),
  obs = rep(1:4, times = 3),
  mi_ever = c(NA, 0, 1, NA, NA, 0, NA, NA, NA, 1, NA, NA),
  diab_ever = c(0, NA, NA, 1, 1, NA, NA, 0, 0, NA, NA, NA)
)

使用data.table(按组处理)快速迭代多个变量:

ever_vars <- c("mi_ever", "diab_ever")

dt[, paste0(ever_vars, "_2") := lapply(.SD, distribute_ever), 
   .SDcols = ever_vars, 
   by = id][]

结果:

    id obs mi_ever diab_ever mi_ever_2 diab_ever_2
 1:  1   1      NA         0         0           0
 2:  1   2       0        NA         0          NA
 3:  1   3       1        NA         1          NA
 4:  1   4      NA         1         1           1
 5:  2   1      NA         1         0           9
 6:  2   2       0        NA         0           9
 7:  2   3      NA        NA        NA           9
 8:  2   4      NA         0        NA           9
 9:  3   1      NA         0        NA           0
10:  3   2       1        NA         1          NA
11:  3   3      NA        NA         1          NA
12:  3   4      NA        NA         1          NA

对于每个输入 "ever" 变量,我们有:

  • 创建了一个新变量,在输入变量名称的末尾附加了“_2”。您也可以按照 42- 指出的那样编辑 "in place",但我喜欢能够仔细检查我的数据。
  • 0 向后进位,1 向后进位。
  • NA 在零之后和一之前(在 id 内)returned 不变。
  • 如果在 1(是的,我有过...)之后有 0(没有,我从来没有...),就像第 2 个人对糖尿病的反应一样,那么函数 returns 9.
  • 如果我们将 check_logic 设置为 FALSE,则 1 将胜出并取代 0