R:用两个最连续值的平均值替换 NA

R: replace NAs with mean of two most contiguous values

一个数据框:

x <- c(3,4,8,10,NA,NA,NA,8,10,10,NA,22)
y <- c(1,6,3,5,NA,44,23,NA,NA,5,34,33)
df <- data.frame(x,y)

x   y
<dbl>   <dbl>
3   1           
4   6           
8   3           
10  5           
NA  NA          
NA  44          
NA  23          
8   NA          
10  NA          
10  5           
NA  34          
22  33  

我想用两个最连续值的平均值替换 NA 值。例如 df[5,2]NA 但我们可以将其替换为 5 和 44 的平均值:

df[5,2] <- (df[4,2]+df[6,2])/2

df[5,2]
[1] 24.5

但是,如果连续值也是NA,则无法完成此操作。用 df[5,1]df[7,1] 之间的平均值替换 df[6,1] 不起作用,因为它们也是 NA。

我想要完成的是确保我用来计算平均值的值是两个最连续的值,而不是 NA。我创建了一个 for 循环来创建我们找到 NAs 的索引的数据框。然后,我创建了代表 NA 旁边的索引的变量,并进行了评估它们是否为 NA 的测试。如果 TRUE 它们是 NA,则索引会根据相对于 NA 索引的位置增加或减少:

x <- as.data.frame(which(is.na(df), arr.ind = TRUE))
str(x)

  'data.frame': 7 obs. of  2 variables:
   $ row: int  5 6 7 11 5 8 9
   $ col: int  1 1 1 1 2 2 2

您将看到一个数据框,其中包含数据集中 NAs 位置的行值和列值。现在我尝试覆盖它们:

for (i in 1:dim(x)[1]) {

    row <- x[i,1]          # First for loop assigns row and column values using the location of NA
    col <- x[i,2]

    b <- row - 1           # Create a list of the indices that precede the NA
    a <- row + 1           # Create a list of the indices that go after the NA

    ifelse(is.na(df[b[i],col]), b[i]-1, b[i])    # If the value in the list is also an NA, keep looking
    ifelse(is.na(df[a[i],col]), a[i]+1, a[i])

    df[row,col] <- (df[b,col]+df[a,col])/2       # Replace the NA with the mean of values where we could 
                                                 # find integers

}

唉,我无法通过所有的 NA。我还没有想出更好的解决方案,因此转向更好的想法。非常感谢!

y <- as.data.frame(which(is.na(df), arr.ind = TRUE))
str(y)

'data.frame':   5 obs. of  2 variables:
 $ row: int  5 6 7 8 9
 $ col: int  1 1 1 2 2

我们可以为此使用 zoo::na.locf() 函数:

x <- c(3,4,8,10,NA,NA,NA,8,10,10,NA,22)
y <- c(1,6,3,5,NA,44,23,NA,NA,5,34,33)
df <- data.frame(x,y)

contiguous_mean <- function(vec) {
    return( (zoo::na.locf(vec) + zoo::na.locf(vec, fromLast = TRUE)) / 2 )
}

apply(df, 2, contiguous_mean)

#        x    y
#  [1,]  3  1.0
#  [2,]  4  6.0
#  [3,]  8  3.0
#  [4,] 10  5.0
#  [5,]  9 24.5
#  [6,]  9 44.0
#  [7,]  9 23.0
#  [8,]  8 14.0
#  [9,] 10 14.0
# [10,] 10  5.0
# [11,] 16 34.0
# [12,] 22 33.0

这里,"locf"代表last observation carried forward,用最后一个观测值替换NA值;使用 fromLast 参数,您可以使用最近的 previous 观察,或最近的 subsequent 观察。我们想要上一个观察值和下一个观察值的平均值,所以我们只需将 fromLast 结果之和除以二,即 TRUEFALSE.

更新:前导或尾随 NAs

G。 Grothendieck 提出了使用 na.locf0() 而不是 na.locf() 的极好建议,以利用前者的 na.rm = FALSE 默认值。当初始值或最后一个值是 而不是 NA 时,这两种方法是等效的,但是当您的列以 NA 开始或结束时,我们需要 na.locf0()。这是一个演示:

z <- c(NA, 1, 2, NA, 3)
contiguous_mean <- function(vec) {
    return( (zoo::na.locf(vec) + zoo::na.locf(vec, fromLast = TRUE)) / 2 )
}
contiguous_mean2 <- function(vec) {
    return( (zoo::na.locf0(vec) + zoo::na.locf0(vec, fromLast = TRUE)) / 2 )
}
## When no leading or trailing NAs, they are equivalent:
all.equal(apply(df, 2, contiguous_mean), apply(df, 2, contiguous_mean2))
# [1] TRUE
## However, when there *are* leading or trailing NAs, the first approach
## causes bad recycling:
contiguous_mean2(z) ## New version
# [1]  NA 1.0 2.0 2.5 3.0
contiguous_mean(z)  ## Old version
# [1] 1.0 1.5 2.0 3.0 2.0
# Warning message:
# In zoo::na.locf(vec) + zoo::na.locf(vec, fromLast = TRUE) :
#   longer object length is not a multiple of shorter object length