【问题标题】:R: replace NAs with mean of two most contiguous valuesR:用两个最连续值的平均值替换 NA
【发布时间】:2020-08-25 13:52:04
【问题描述】:

一个数据框:

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

【问题讨论】:

  • 为了确保我理解您的问题,df$x 中的第五、第六和第七个值(第一列中的三个连续的NA 值)都应该是 9 ( (10+8 )/2 ),对吗?
  • @duckmayr 你是对的。

标签: r replace mean na


【解决方案1】:

我们可以为此使用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,它取代了@ 987654323@ 值与最后观察到的值;使用fromLast 参数,您可以使用最近的previous 观察,或最近的subsequent 观察。我们想要上一个前一个观察值和下一个后续观察值的平均值,所以我们只需将fromLast 的结果之和除以TRUEFALSE

更新:前导或尾随 NAs

G. Grothendieck 提出了极好的建议,即使用 na.locf0() 而不是 na.locf() 来使用前者的 na.rm = FALSE 默认值。当初始值或最后一个值不是NA时,这两种方法是等价的,但是当您的列以NAs 开头或结尾时,我们需要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

【讨论】:

    猜你喜欢
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    • 2019-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-04
    相关资源
    最近更新 更多