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
我有一个数据框,在 '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