如何用缺失值前后的平均值填充向量中的缺失值

how to fill missing values in a vector with the mean of value before and after the missing one

目前我正在尝试在 R 中估算向量中的值。条件 的归责是。

# example one
input_one = c(1,NA,3,4,NA,6,NA,NA)

# example two
input_two = c(NA,NA,3,4,5,6,NA,NA)

# example three
input_three = c(NA,NA,3,4,NA,6,NA,NA)

我开始编写代码来检测可以 归咎于。但是我遇到了以下问题。

# incomplete function to detect the values
sapply(split(!is.na(input[c(rbind(which(is.na(c(input)))-1, which(is.na(c(input)))+1))]), 
             rep(1:(length(!is.na(input[c(which(is.na(c(input)))-1, which(is.na(c(input)))+1)]))/2), each = 2)), all)

然而,这只会检测到可能是 imputable 并且它只适用于示例一。它是不完整的并且 不幸的是超级难以阅读和理解。

如有任何帮助,我们将不胜感激。

我们可以为此使用 dplyrs laglead 函数:

input_three = c(NA,NA,3,4,NA,6,NA,NA)

library(dplyr)
ifelse(is.na(input_three) & lead(input_three) > lag(input_three),
       (lag(input_three)  + lead(input_three))/ 2,
       input_three)

返回:

[1] NA NA  3  4  5  6 NA NA

编辑

解释:

我们使用 ifelse,它是 if 的向量化版本。 IE。 ifelse 中的所有内容都将应用于向量的每个元素。 首先,我们测试元素是否为 NA 以及下一个元素是否大于前一个元素。要获取前一个和后一个元素,我们可以使用 dplyr leadlag 函数:

lag 向右偏移向量(默认为 1 步):

lag(1:5)

Returns:

[1] NA  1  2  3  4

lead 向左偏移向量:

lead(1:5)

Returns:

[1]  2  3  4  5 NA

现在到 ifelse 的 'test' 子句:

is.na(input_three) & lead(input_three) > lag(input_three)

哪个 returns:

[1]    NA    NA FALSE FALSE  TRUE FALSE    NA    NA

然后如果 ifelse 子句的计算结果为 TRUE 我们想要 return 前后元素的总和除以 2,否则 return 原始元素

这是使用 zoo::rollapply() 的替代方法:

library(zoo)

fill_sandwiched_na <- function(f) rollapply(f, 3, FUN = function(x) {
  y <- mean(x[-2]); if(is.na(y)) x[2] else y
}, fill = NA, partial = TRUE)

fill_sandwiched_na(input_one)
[1]  1  2  3  4  5  6 NA NA

fill_sandwiched_na(input_two)
[1] NA NA  3  4  5  6 NA NA

fill_sandwiched_na(input_three)
[1] NA NA  3  4  5  6 NA NA

这是一个使用 imputeTS 库的示例。它考虑了序列中的多个 NA,确保在下一个有效观察值大于最后一个有效观察值时计算平均值,并且还会忽略开头和结尾的 NA

library(imputeTS)
myimpute <- function(series) {
    # Find where each NA is
    nalocations <- is.na(series)
    # Find the last and the previous observation for each row
    last1 <- lag(series)
    next1 <- lead(series)
    # Carry forward the last and next observations over sequences of NA
    # Each row will then get a last and next that can be averaged
    cflast <- na_locf(last1, na_remaining = 'keep')
    cfnext <- na_locf(next1, option = 'nocb', na_remaining = 'keep')
    # Make a data frame 
    df <- data.frame(series, nalocations, last1, cflast, next1, cfnext)
    # Calculate the mean where there is currently a NA
    # making sure that the next is greater than the last
    df$mean <- ifelse(df$nalocations, ifelse(df$cflast < df$cfnext, (df$cflast+df$cfnext)/2, NA), NA)
    imputedseries <- ifelse(df$nalocations, ifelse(!is.na(df$mean), df$mean, NA), series)
    #list(df,  imputedseries) # comment this in and return it to see the intermediate data frame for debugging
    imputedseries
}
myimpute(c(NA,NA,3,4,NA,NA,6,NA,NA,8,NA,7,NA,NA,9,NA,11,NA,NA))

# [1] NA NA  3  4  5  5  6  7  7  8 NA  7  8  8  9 10 11 NA NA

imputeTS 包中还有 na_ma 函数用于估算移动平均线。

在您的情况下,这将使用以下设置:

na_ma(x, k = 1, weighting = "simple")

  • k = 1(表示考虑了 NA 之前的 1 个值和 NA 之后的 1 个值)
  • weighting = "simple"(计算这两个值的平均值)

基本上只需 1 行代码就可以很容易地应用它:

library(imputeTS)
na_ma(yourData, k = 1, weighting = "simple") 

您也可以选择考虑 NA 前后的更多值,例如k=3。如果您考虑每一方的值超过 1 个,则有趣的功能是可以选择不同的权重,例如weighting = "linear" weights 在算术级数中减少(线性加权移动平均线) - 这意味着它们的值离 NA 越远,它们的影响就越小。