用最接近的值替换R中的NA

geo*_*try 28 r missing-data na

我正在寻找类似于包na.locf()中的东西zoo,但不是总是使用之前的NA值我想使用最接近的NA值.一些示例数据:

dat <- c(1, 3, NA, NA, 5, 7)
Run Code Online (Sandbox Code Playgroud)

替换NAna.locf(3继续):

library(zoo)
na.locf(dat)
# 1 3 3 3 5 7
Run Code Online (Sandbox Code Playgroud)

na.locffromLast组到TRUE(5向后携带):

na.locf(dat, fromLast = TRUE)
# 1 3 5 5 5 7
Run Code Online (Sandbox Code Playgroud)

但我希望使用最接近的NA值.在我的例子中,这意味着3应该被转发到第一个NA,而5应该被转发到第二个NA:

1 3 3 5 5 7
Run Code Online (Sandbox Code Playgroud)

我有一个编码的解决方案,但想确保我没有重新发明轮子.有什么东西已经浮动了吗?

仅供参考,我目前的代码如下.也许如果不出意外,有人可以建议如何提高效率.我觉得我错过了一个明显的改进方法:

  na.pos <- which(is.na(dat))
  if (length(na.pos) == length(dat)) {
    return(dat)
  }
  non.na.pos <- setdiff(seq_along(dat), na.pos)
  nearest.non.na.pos <- sapply(na.pos, function(x) {
    return(which.min(abs(non.na.pos - x)))
  })
  dat[na.pos] <- dat[non.na.pos[nearest.non.na.pos]]
Run Code Online (Sandbox Code Playgroud)

要回答以下smci的问题:

  1. 不,任何条目都可以是NA
  2. 如果全部都是NA,请保持原样
  3. 不.我当前的解决方案默认为左手最近的值,但没关系
  4. 这些行通常是几十万个元素,因此理论上上限为几十万个.实际上,这里和那里只有一些,通常只有一个.

更新所以事实证明我们完全朝着不同的方向前进,但这仍然是一个有趣的讨论.谢谢大家!

flo*_*del 22

这是一个非常快的.它用于findInterval查找NA原始数据中每个位置应考虑的两个位置:

f1 <- function(dat) {
  N <- length(dat)
  na.pos <- which(is.na(dat))
  if (length(na.pos) %in% c(0, N)) {
    return(dat)
  }
  non.na.pos <- which(!is.na(dat))
  intervals  <- findInterval(na.pos, non.na.pos,
                             all.inside = TRUE)
  left.pos   <- non.na.pos[pmax(1, intervals)]
  right.pos  <- non.na.pos[pmin(N, intervals+1)]
  left.dist  <- na.pos - left.pos
  right.dist <- right.pos - na.pos

  dat[na.pos] <- ifelse(left.dist <= right.dist,
                        dat[left.pos], dat[right.pos])
  return(dat)
}
Run Code Online (Sandbox Code Playgroud)

在这里我测试它:

# sample data, suggested by @JeffAllen
dat <- as.integer(runif(50000, min=0, max=10))
dat[dat==0] <- NA

# computation times
system.time(r0 <- f0(dat))    # your function
# user  system elapsed 
# 5.52    0.00    5.52
system.time(r1 <- f1(dat))    # this function
# user  system elapsed 
# 0.01    0.00    0.03
identical(r0, r1)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)


smc*_*mci 6

代码如下.最初的问题并没有完全明确,我曾要求做出这些澄清:

  1. 是否保证至少第一个和/或最后一个条目是非NA的?[没有]
  2. 如果连续的所有条目都是NA,该怎么办?[原样]
  3. 你是否关心如何分裂关系,即如何对待中间NA 1 3 NA NA NA 5 7[不关心/离开]
  4. 你在连续的最长连续跨度上有一个上限(S)吗?(如果S很小,我正在考虑递归解决方案.或者ifelse如果S很大且行数和列数很大的数据帧解决方案.)[最坏情况S可能在病理上很大,因此不应使用递归]

geoffjentry,你的解决方案你的瓶颈将是串行计算nearest.non.na.pos和序列分配dat[na.pos] <- dat[non.na.pos[nearest.non.na.pos]] 对于长度G的大差距,我们真正需要计算的是第一个(G/2,向上舍入)项目从左边填充,从右边休息.(我可以发布一个答案,ifelse但看起来很相似.)你的标准运行时,大O效率,临时内存使用或代码易读性?

Coupla可能的调整:

  • 只需要计算N <- length(dat)一次
  • 常见速度增强:if (length(na.pos) == 0)跳过行,因为它没有NA
  • if (length(na.pos) == length(dat)-1) (稀少)情况下只有一个非NA条目,因此我们用它填充整行

大纲解决方案

遗憾的是na.locf不适用于整个数据帧,你必须使用sapply,row-wise:

na.fill_from_nn <- function(x) {
  row.na <- is.na(x)
  fillFromLeft <- na.locf(x, na.rm=FALSE) 
  fillFromRight <- na.locf(x, fromLast=TRUE, na.rm=FALSE)

  disagree <- rle(fillFromLeft!=fillFromRight)
  for (loc in (disagree)) { ...  resolve conflicts, row-wise }
}

sapply(dat, na.fill_from_nn)
Run Code Online (Sandbox Code Playgroud)

或者,因为正如你所说的那样,连续的NAs是罕见的,所以要快速和愚蠢ifelse地从左边填充孤立的NA.这将以数据帧方式操作=>使公共情况快速.然后使用行方式for循环处理所有其他情况.(这会影响很长一段时间内中间元素的抢七,但你说你不在乎.)


小智 5

我喜欢所有严格的解决方案。虽然不是直接询问什么,但我发现这篇文章正在寻找一种用插值填充 NA 值的解决方案。审阅这篇文章后,我发现 na.fill 在一个zoo对象(向量、因子或矩阵)上:

z <- c(1,2,3,4,5,6,NA,NA,NA,2,3,4,5,6,NA,NA,4,6,7,NA)
z1 <- zoo::na.fill(z, "extend")
Run Code Online (Sandbox Code Playgroud)

注意 NA 值的平滑过渡

round(z1, 0)
#>  [1] 1 2 3 4 5 6 5 4 3 2 3 4 5 6 5 5 4 6 7 7
Run Code Online (Sandbox Code Playgroud)

也许这可以帮助