【问题标题】:Replace NA row with non-NA value from previous row and certain column用前一行和某列中的非 NA 值替换 NA 行
【发布时间】:2014-05-09 20:53:58
【问题描述】:

我有一个矩阵,其中行的所有列都可以有 NA。我想用前一行的非 NA 值和第 K 列替换这些 NA 行。

例如这个矩阵:

      [,1] [,2]
 [1,]   NA   NA
 [2,]   NA   NA
 [3,]    1    2
 [4,]    2    3
 [5,]   NA   NA
 [6,]   NA   NA
 [7,]   NA   NA
 [8,]    6    7
 [9,]    7    8
[10,]    8    9

必须转换成这个非 NA 矩阵,这里我们使用第 2 列进行替换:

      [,1] [,2]
 [1,]   NA   NA
 [2,]   NA   NA
 [3,]    1    2
 [4,]    2    3
 [5,]    3    3
 [6,]    3    3
 [7,]    3    3
 [8,]    6    7
 [9,]    7    8
[10,]    8    9

我为此写了一个函数,但使用循环:

# replaces rows which contains all NAs with non-NA values from previous row and K-th column
na.replace <- function(x, k) {
    cols <- ncol(x)
    for (i in 2:nrow(x)) {
        if (sum(is.na(x[i - 1, ])) == 0 && sum(is.na(x[i, ])) == cols) {
            x[i, ] <- x[i - 1 , k]
        }
    }
    x
}

似乎这个函数工作正常,但我想避免这些循环。任何人都可以建议,我如何在不使用循环的情况下进行替换?

更新

agstudy 建议使用自己的矢量化非循环解决方案:

na.replace <- function(mat, k){
  idx       <-  which(rowSums(is.na(mat)) == ncol(mat))
  mat[idx,] <- mat[ifelse(idx > 1, idx-1, 1), k]
  mat
}

但与我的循环解决方案相比,此解决方案返回不同且错误的结果。为什么会发生这种情况?理论上循环和非循环的解决方案是相同的。

【问题讨论】:

    标签: r na


    【解决方案1】:

    试试这个功能。我们可以在向量中的任何位置替换 NA

    NA.replace <-function(x) {
           i <- cumprod(is.na(x))
           x[!!i] <- x[which.min(i)]
            if (length(x) > 0L) {
                non.na.idx <- which(!is.na(x))
                if (is.na(x[1L])) {
                    non.na.idx <- c(1L, non.na.idx)
                }
                rep.int(x[non.na.idx], diff(c(non.na.idx, length(x) + 1L)))
            }  
    }
    
    NA.replace(c(NA, 1, 2, NA, NA, 3, NA, NA, 4, NA))
    
    # [1] 1 1 2 2 2 3 3 3 4 4
    

    【讨论】:

      【解决方案2】:

      我会在一个循环中使用na.locf 函数,该函数只使用下一列来生成一个替换值向量。但是,如果您的矩阵很大,这可能不是很有效。

      library(zoo)
      
      m <- cbind(
          c(NA, NA, 1, 2, NA, 4, NA, 6, 7, 8),
          c(NA, NA, 2, 3, NA, 5, NA, 7, 8, 9)
      )
      
      m[, ncol(m)] <- na.locf(m[, ncol(m)], na.rm=FALSE)
      
      for (i in seq(ncol(m)-1, 1)) {
          replacement_values = na.locf(m[, i+1], na.rm=FALSE)
          m[is.na(m[, i]), i] <- replacement_values[is.na(m[, i])]    
      }
      

      【讨论】:

      • 起初,我没有看到最后一列也发生了插补。你打算如何做到这一点?最后一列是常规的na.locf,还是[5,2][7,2] 的值取自同一行的上一列?
      • 是的,因为最后一列的结果与 na.locf 相同,因为我们将最后一列的 NA 值替换为来自同一最后一列的先前非 NA 值。这只是特例。
      【解决方案3】:

      编辑:我完全改变了基于 na.locf 的第一个解决方案是

      这里有一个新的矢量化解决方案:

      idx <- which(rowSums(is.na(mat)) == ncol(mat))
      mat[idx,1:2]= mat[ifelse(idx>1,idx-1,1),2]
      
           X..1. X..2.
      [1,]     NA    NA
      [2,]     NA    NA
      [3,]      1     2
      [4,]      2     3
      [5,]      3     3
      [6,]      4     5
      [7,]      5     5
      [8,]      6     7
      [9,]      7     8
      [10,]     8     9
      

      您可以将其包装在一个函数中:

      function(mat,k){
        idx       <-  which(rowSums(is.na(mat)) == ncol(mat))
        mat[idx,] <- mat[ifelse(idx>1,idx-1,1),k]
      }
      

      【讨论】:

      • 我知道na.locf,但是这个函数不适合我的问题。它用同一列中以前的非 NA 替换 NA。您的输出矩阵错误:第 5 行必须有 (3, 3) 值,第 7 行 - (5, 5) 值。
      • 我发现,如果至少 2 个连续行具有 NA,例如第 6 行和第 7 行,那么您的解决方案仅替换第 6 行而不是第 7 行。我用更好的变体更新了主题中的示例。
      【解决方案4】:

      终于实现了我自己的矢量化版本。它返回预期的输出:

      na.replace <- function(x, k) {
          isNA <- is.na(x[, k])
          x[isNA, ] <- na.locf(x[, k], na.rm = F)[isNA]
          x
      }
      

      更新

      更好的解决方案,无需任何软件包

      na.lomf <- function(x) {
          if (length(x) > 0L) {
              non.na.idx <- which(!is.na(x))
              if (is.na(x[1L])) {
                  non.na.idx <- c(1L, non.na.idx)
              }
              rep.int(x[non.na.idx], diff(c(non.na.idx, length(x) + 1L)))
          }
      }
      
      na.lomf(c(NA, 1, 2, NA, NA, 3, NA, NA, 4, NA))
      # [1] NA  1  2  2  2  3  3  3  4  4
      

      【讨论】:

        猜你喜欢
        • 2023-03-13
        • 1970-01-01
        • 2020-05-18
        • 2013-01-25
        • 2011-12-05
        相关资源
        最近更新 更多