【问题标题】:Conditionally replace missing values depending on surrounding non-missing values根据周围的非缺失值有条件地替换缺失值
【发布时间】:2018-09-16 16:25:15
【问题描述】:

我正在尝试替换向量中的缺失值 (NA)。两个相等数字之间的NA 被该数字替换。 NA 在两个不同的值之间,应保持在 NA。例如,给定向量“a”,我希望它是“b”。

a = c(1, NA, NA, NA, 1, NA, NA, NA, 2, NA, NA, 2, 3, NA, NA, 3)
b = c(1, 1, 1, 1, 1, NA, NA, NA, 2, 2, 2, 2, 3, 3, 3, 3)

如您所见,NA 的第二次运行,在值 12 之间,没有被替换。

有没有办法向量化计算?

【问题讨论】:

  • 如果向量的开头或结尾有NA,他们会留下NA吗?
  • 是的。他们留在北美。

标签: r vectorization na missing-data


【解决方案1】:

OP 要求 vecgorized 解决方案,所以这里有一个可能的矢量化基础 R 解决方案(没有 for 循环),它也可以处理领先/滞后 NA 的情况

# Define a vector with Leading/Lagging NAs
a <- c(NA, NA, 1, NA, NA, NA, 1, NA, NA, NA, 2, NA, NA, 2, 3, NA, NA, 3, NA, NA)

# Save the boolean vector as we are going to reuse it a lot
na_vals <- is.na(a)

# Find the NAs location compared to the non-NAs
ind <- findInterval(which(na_vals), which(!na_vals))

# Find the consecutive values that equal
ind2 <- which(!diff(a[!na_vals]))

# Fill only NAs between equal consequtive files
a[na_vals] <- a[!na_vals][ind2[match(ind, ind2)]]
a
# [1] NA NA  1  1  1  1  1 NA NA NA  2  2  2  2  3  3  3  3 NA NA

大向量的一些时间比较

# Create a big vector
set.seed(123)
a <- sample(c(NA, 1:5), 5e7, replace = TRUE)

############################################
##### Cainã Max Couto-Silva

fill_data <- function(vec) {

  for(l in unique(vec[!is.na(vec)])) {

    g <- which(vec %in% l)

    indexes <- list()

    for(i in 1:(length(g) - 1)) {
      indexes[[i]] <- (g[i]+1):(g[i+1]-1)
    }

    for(i in 1:(length(g) - 1)) { 
      if(all(is.na(vec[indexes[[i]]]))) {
        vec[indexes[[i]]] <- l
      }
    }
  }

  return(vec)
}

system.time(res <- fill_data(a))
#   user  system elapsed 
#  81.73    4.41   86.48 

############################################
##### Henrik

system.time({
  a_ap <- na.approx(a, na.rm = FALSE)
  a_locf <- na.locf(a, na.rm = FALSE)
  a[which(a_ap == a_locf)] <- a_ap[which(a_ap == a_locf)]
})
#  user  system elapsed 
# 12.55    3.39   15.98 

# Validate
identical(res, as.integer(a))
# [1] TRUE

############################################
##### David

## Recreate a as it been overridden
set.seed(123)
a <- sample(c(NA, 1:5), 5e7, replace = TRUE)

system.time({
  # Save the boolean vector as we are going to reuse it a lot
  na_vals <- is.na(a)

  # Find the NAs location compaed to the non-NAs
  ind <- findInterval(which(na_vals), which(!na_vals))

  # Find the consecutive values that equl
  ind2 <- which(!diff(a[!na_vals]))

  # Fill only NAs between equal consequtive files
  a[na_vals] <- a[!na_vals][ind2[match(ind, ind2)]]
})
# user  system elapsed 
# 3.39    0.71    4.13 

# Validate
identical(res, a)
# [1] TRUE

【讨论】:

    【解决方案2】:

    您可以使用zoo 包中的便利功能。在这里,我们替换原始向量中的NA,其中插值(由na.approx 创建)等于“最后的观察结转”(由na.locf 创建):

    library(zoo)
    a_ap <- na.approx(a)
    a_locf <- na.locf(a)
    a[which(a_ap == a_locf)] <- a_ap[which(a_ap == a_locf)]
    a
    # [1]  1  1  1  1  1 NA NA NA  2  2  2  2  3  3  3  3
    

    要考虑前导和尾随NA,请添加na.rm = FALSE

    a <- c(NA, 1, NA, NA, NA, 1, NA, NA, NA, 2, NA, NA, 2, 3, NA, NA, 3, NA)
    
    a_ap <- na.approx(a, na.rm = FALSE)
    a_locf <- na.locf(a, na.rm = FALSE)
    a[which(a_ap == a_locf)] <- a_ap[which(a_ap == a_locf)]
    a
    # [1] NA  1  1  1  1  1 NA NA NA  2  2  2  2  3  3  3  3 NA
    

    【讨论】:

      【解决方案3】:

      你可以制作这样的函数:

      fill_data <- function(vec) {
      
        for(l in unique(vec[!is.na(vec)])) {
      
          g <- which(vec %in% l)
      
          indexes <- list()
      
          for(i in 1:(length(g) - 1)) {
            indexes[[i]] <- (g[i]+1):(g[i+1]-1)
          }
      
          for(i in 1:(length(g) - 1)) { 
            if(all(is.na(vec[indexes[[i]]]))) {
              vec[indexes[[i]]] <- l
            }
          }
        }
      
        return(vec)
      }
      

      运行函数:

      a = c(1, NA, NA, NA, 1, NA, NA, NA, 2, NA, NA, 2, 3, NA, NA, 3)
      
      fill_data(a)
      [1]  1  1  1  1  1 NA NA NA  2  2  2  2  3  3  3  3
      

      如果你有一个在不同地方有值的向量,它也可以工作:

      ab = c(1, NA, NA, NA, 1, NA, NA, NA, 1, NA, 2, NA, NA, NA, 2, NA , 1, NA, 1, 3, NA, NA, 3)
      
      fill_data(ab)
      [1]  1  1  1  1  1  1  1  1  1 NA  2  2  2  2  2 NA  1  1  1  3  3  3  3
      

      说明:

      首先,您找到唯一的非 NA 值。

      然后它获取每个唯一非NA值的索引并获取它们之间的值;

      然后它会测试这些值是否都是 NA,如果是,则将它们替换为级别的值。

      【讨论】:

      • 技术上不符合 OP 的要求,因为无论如何输出都是字符向量。如果您在函数的最开始添加vec_cls &lt;- class(vec) 并在返回之前添加class(vec) &lt;- vec_cls 将修复它。或者更好的是使用unique(vec[!is.na(vec)]) 代替levels(factor(vec)),然后使用g &lt;- which(vec %in% l) 代替grep
      • 是的,我注意到返回的字符向量,但我认为这根本不是问题,因为之后res &lt;- as.cls(res) 很容易修复它。在函数内部修复它肯定更实用。随意编辑答案(这是我在这里的第一个答案)。亲切的问候。
      • 我喜欢通过不进入级别和 grep 来避免整个问题的编辑。可能也会更有效率。
      • 这个非常未矢量化,而 OP 明确要求矢量化解决方案
      猜你喜欢
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 2020-01-04
      相关资源
      最近更新 更多