将 NA 替换为 R 向量中先前数字的修改版本

问题描述

我有一个带有一些 NA 的向量,我想用之前的非 NA 值减去 0.1 来替换其中的一些 NA。如果 NAs 字符串长于某个长度(例如,2),我也不想替换 NAs。举个例子

x <- c(1:3,NA,4,5,6,NA)

我想制作一个看起来像的矢量

x_prime <- c(1:3,2.9,3.9,3.8,5.9)

打印出来的样子:

> x_prime
 [1] 1.0 2.0 3.0 2.9 4.0 3.9 3.8 5.0  NA  NA  NA 6.0  5.9

作为一个额外的复杂因素,我想跟踪我修改的索引,所以我还想要一个看起来像的向量

idx <- c(4,7,13)

如果是 NA 中的第一个位置(以及所有领先的 NA),我们可以保留它并且什么都不做。

我在诸如 this 之类的 SO 上发现了一些类似的问题,并且我尝试了与那里介绍的功能类似的功能,但没有成功。有任何想法吗?提前致谢。

解决方法

带有 ave 的基本 R 选项:

len <- 2
x1 <- ave(x,cumsum(!is.na(x)),FUN = function(v) {
  if(length(v) > len + 1) v 
  else v[1] - seq(0,by= 0.1,length.out = length(v))
  })

x1
#[1] 1.0 2.0 3.0 2.9 4.0 3.9 3.8 5.0  NA  NA  NA 6.0 5.9

我们将 NA 值与第一个非 NA 值一起创建组并在 ave 中使用它。如果组长度大于 len + 1+ 1,因为第一个值不是每个组中的 NA),那么我们不会更改组中的任何内容,否则我们减去 0、0.1、 0.2 距离组中的第一个值。


要获得被替换的位置,找出 NA 中的 x 而不是 NA 中的 x1

which(is.na(x) & !is.na(x1))
#[1]  4  6  7 13
,

这是一个带有 diffcumsumsplit 的选项

library(zoo)
lst1 <- split(x,cumsum(c( diff(!is.na(x)) < 0,TRUE)))
unname(unlist(lapply(lst1,function(x) if(sum(is.na(x)) <= 2) 
      na.locf0(x) -seq(0,length.out = length(x),by = 0.1) else x)))
#[1] 1.0 1.9 3.0 2.9 4.0 3.9 3.8 5.0  NA  NA  NA 6.0  NA

对于第二种情况

as.vector(unlist(sapply(split(seq_along(x) * is.na(x),TRUE))),function(x)  x[x != 0 & sum(x != 0) <=2])))
#[1]  4  6  7 13
,

使用包 imputeTS 使用 na_locf 和参数 maxgap 的版本:

library("imputeTS")
x_prime <- na_locf(x,maxgap = 2)
idx <- which(is.na(imp) != is.na(x))
x_prime[idx] <- x_prime[idx] - 0.1

结果:

x_prime
[1] 1.0 2.0 3.0 2.9 4.0 3.9 3.9 5.0  NA  NA  NA 6.0 5.9

idx
[1]  4  6  7 13

编辑: 简短的补充,似乎我对“用之前的非 NA 值减去 0.1 替换 NA”的解释不同。不确定这是否是故意的,但您似乎更喜欢减去 0.1,因为之前的值是推算值。