r 用两个先前值的平均值替换每个缺失值

r replace each missing value with a mean of two previous values

我有一个数据框,在 'myvalues' 列中有一些 NA:

x <- data.frame(mydates = as.Date(c("2018/04/01","2018/04/02","2018/04/03","2018/04/04",
                                                     "2018/04/05","2018/04/06","2018/04/07","2018/04/08",
                                                     "2018/04/09","2018/04/10","2018/04/11")),
                                 myvalues = c(2.3, NA, 2.1, 2.2, NA, 2.4, 2.3, 2.1, NA, NA, 2.6))
x

我想用前两个值的平均值替换每个 NA。对于第 2 行中的 NA,它应该等于第 1 行中的值。 我可以使用 'for' 循环通过 x$myvalues 中的 NA 来完成。然而,它非常慢,我正在寻找一个快速的解决方案,因为我必须对像 x 这样的小数据帧上的数百万做同样的事情。

非常感谢!

矢量化解决方案,如果您需要速度:

x = c(2.3, NA, 2.1, 2.2, NA, 2.4, 2.3, 2.1, NA, NA, 2.6)
y <- which(!is.na(x))[findInterval(which(is.na(x)), which(!is.na(x)))]
y[y==1] = NA
x[which(is.na(x))] = (x[y-1] + x[y])/2
x
# [1] 2.30   NA 2.10 2.20 2.15 2.40 2.30 2.10 2.20 2.20 2.60

以上版本将 NA 作为第二个值,因为在第一个 NA 之前没有 2 个值可以取平均值。相反,如果您希望这个 NA 是它前面唯一值的平均值,那么我们可以这样做:

y <- which(!is.na(x))[findInterval(which(is.na(x)), which(!is.na(x)))]
x[which(is.na(x))] = (x[pmax(1,y-1)] + x[y])/2
# [1] 2.30 2.30 2.10 2.20 2.15 2.40 2.30 2.10 2.20 2.20 2.60

一个 data.table 应该相当快速的解决方案:

library(data.table)

x <-
  data.frame(
    mydates = as.Date(
      c(
        "2018/04/01",
        "2018/04/02",
        "2018/04/03",
        "2018/04/04",
        "2018/04/05",
        "2018/04/06",
        "2018/04/07",
        "2018/04/08",
        "2018/04/09",
        "2018/04/10",
        "2018/04/11"
      )
    ),
    myvalues = c(2.3, NA, NA, 2.2, NA, NA, 2.3, NA, NA, NA, 2.6)
  )

# Carry forward mean of last two non-missing values
setDT(x)

x[, segment := cumsum(!is.na(myvalues))]
x[, last1 := myvalues[1], by = segment]
x[!is.na(myvalues), segment2:=segment]
x[is.na(myvalues), segment2:=segment-1]
x[, last2 := myvalues[1], by = segment2]
x[, repl:=rowMeans(.SD, na.rm=T), .SDcols=c("last1", "last2")]
x[, myvalues2:=myvalues]
x[is.na(myvalues2) & !is.nan(repl), myvalues2:=repl]

x[, list(mydates, myvalues, myvalues2)]
# mydates myvalues myvalues2
# 1: 2018-04-01      2.3      2.30
# 2: 2018-04-02       NA      2.30
# 3: 2018-04-03       NA      2.30
# 4: 2018-04-04      2.2      2.20
# 5: 2018-04-05       NA      2.25
# 6: 2018-04-06       NA      2.25
# 7: 2018-04-07      2.3      2.30
# 8: 2018-04-08       NA      2.25
# 9: 2018-04-09       NA      2.25
# 10: 2018-04-10       NA      2.25
# 11: 2018-04-11      2.6      2.60

您可以减少Reduce函数。在这种情况下,例如,最后一个 NA 将是前两个值的平均值,但它会先填充前一个值然后使用它来获取当前值

x$myvalues=Reduce(function(x,y)if(is.na(y))c(x,mean(tail(x,2))) else c(x,y),x$myvalues)
> x
      mydates myvalues
1  2018-04-01     2.30
2  2018-04-02     2.30
3  2018-04-03     2.10
4  2018-04-04     2.20
5  2018-04-05     2.15
6  2018-04-06     2.40
7  2018-04-07     2.30
8  2018-04-08     2.10
9  2018-04-09     2.20
10 2018-04-10     2.15
11 2018-04-11     2.60