【问题标题】:R carry forward and backward conditionallyR有条件地前进和后退
【发布时间】:2017-05-26 21:37:00
【问题描述】:

我有一组变量,其中包含有关某人是否曾经患有某些健康状况的数据。例如,“你有没有心脏病发作过?”

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

如果他们在观察 2 中说“不”,那么在观察 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

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

Carry last Factor observation forward and backward in group of rows in R

Forward and backward fill data frame in R

making a "dropdown" function in R

感谢任何帮助。

【问题讨论】:

    标签: r zoo


    【解决方案1】:

    基本上,我将第一个 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
    

    如果您需要抑制应该可能的警告。

    【讨论】:

      【解决方案2】:

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

      • 处理了警告“min 没有非缺失参数;将 Infno 非缺失参数返回给 max;返回 -Inf”。
      • 将单独的函数组合成一个函数(尽管单独的函数对学习非常有用)。
      • 添加了一个可选的check_logic 参数。为 TRUE 时,如果 0 出现在 1 之后,该函数将返回 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- 指出的那样编辑“就地”,但我喜欢能够仔细检查我的数据。
      • 0 向后进位,1 及时进位。
      • 零之后和之前(在 id 内)的 NA 原封不动地返回。
      • 如果在 1(是的,我有过 ...)之后有一个 0(不,我从来没有...),就像第 2 个人关于糖尿病的回答一样,则函数返回9 的。
      • 如果我们将 check_logic 设置为 FALSE,那么 1 将胜出并替换 0

      【讨论】:

        猜你喜欢
        • 2017-11-05
        • 1970-01-01
        • 1970-01-01
        • 2013-05-13
        • 2011-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多