迭代多个变量时如何与 NA 进行比较

How to compare to NA when iterating over multiple variables

我将提供很多并非绝对必要的背景知识(我欢迎提出改进建议),因此如果您只想查看我的主要问题,可以滚动到最后。

背景

我有一个数据集,我想根据一些规则在其中填充多个变量的缺失值。一些示例数据:

library(data.table)
library(tidyverse)

dt <- data.frame(id = c('a', 'a', 'a', 'a', 'a',
                        'b', 'b', 'b', 'b', 'b'),
                 var1 = c(1, 1, NA, 1, NA,
                          NA, 5, 5, NA, 5),
                 var2 = c('hi', 'hi', NA, 'hi', 'hi',
                          'bye', NA, 'bye', 'bye', NA),
                 year = c(2005:2009,
                          1995:1998, 2002))

请注意,实际数据很大:数以百万计的观察值,至少 30-40 个变量,我将填写大约 10 列,因此采用 data.table 方法。

我目前使用查找超前和滞后并检查我的条件的函数填写它们,如下所示:

fill.in <- function(var, yr, finyr) {
  leadv <- lead(var, order_by = yr)
  lagv <- lag(var, order_by = yr)
  
  leadyr <- lead(yr, order_by = yr)
  lagyr <- lag(yr, order_by = yr)
  
  # ------- build the updated var w/ sequential conditions
  # keep the var as it is if not missing
  try1 <- ifelse(test = !is.na(var),
                 yes = var,
                 no = NA)
  
  # fill in if the lead and lag match and no more than 2 missing years
  try2 <- ifelse(test = is.na(try1) & leadv == lagv &
                        abs(leadyr-lagyr) <= 3 &
                        !is.na(leadv),
                 yes = leadv,
                 no = try1)
  
  # fill in with the lag if it's the final year of observed data
  ifelse(test = is.na(try2) & yr == finyr &
                abs(yr-lagyr) <= 3 & !is.na(lagv),
         yes = lagv,
         no = try2)
}

我用 data.table.SD:

来称呼它
setDT(dt)

dt[, finalyr := max(year), by = id]

# variables to iterate over and the new column names
fill.in.vs <- c('var1', 'var2')
fixed.vnames <- paste0('fixed.', fill.in.vs)

# I don't want factor values, so make strings characters
dt$var2 <- as.character(dt$var2)

dt[, (fixed.vnames) := sapply(.SD,
                              FUN = fill.in,
                              yr = year,
                              finyr = finalyr,
                              simplify = FALSE, USE.NAMES = FALSE),
   by = id, .SDcols = fill.in.vs]

这给了我想要的数据:

    id var1 var2 year finalyr fixed.var1 fixed.var2
 1:  a    1   hi 2005    2009          1         hi
 2:  a    1   hi 2006    2009          1         hi
 3:  a   NA <NA> 2007    2009          1         hi
 4:  a    1   hi 2008    2009          1         hi
 5:  a   NA   hi 2009    2009          1         hi
 6:  b   NA  bye 1995    2002         NA        bye
 7:  b    5 <NA> 1996    2002          5        bye
 8:  b    5  bye 1997    2002          5        bye
 9:  b   NA  bye 1998    2002         NA        bye
10:  b    5 <NA> 2002    2002          5       <NA>

主要问题

现在回答我的实际问题:我想要一个已更新行的指示器。我不认为我使用 data.table.SD 更新的方式允许制作配对变量(我的意思是在每次迭代中产生两个或三个不同的变量),所以我想我只会标记基本上 original.var != fixed.var.

的观察结果

这会遇到问题,因为 R 不喜欢将值与 NAs 进行比较。 (参见 here for example.

我已经尝试为此实施一些解决方案,但我一直 运行 陷入尝试通过变量列表执行此操作的问题。例如,以下工作可以识别哪些相同或不同:

# this works:
dt$updated.var1 <- mapply(identical, dt$var1, dt$fixed.var1,
                          SIMPLIFY = FALSE, USE.NAMES = FALSE)

dt[,c('var1', 'fixed.var1', 'updated.var1')]
    var1 fixed.var1 updated.var1
 1:    1          1         TRUE
 2:    1          1         TRUE
 3:   NA          1        FALSE
 4:    1          1         TRUE
 5:   NA          1        FALSE
 6:   NA         NA         TRUE
 7:    5          5         TRUE
 8:    5          5         TRUE
 9:   NA         NA         TRUE
10:    5          5         TRUE

但是当我尝试将它与 data.table 和变量列表一起使用时,它 returns 全部 FALSE 用于两个变量:

# indicate updated observations
update.names <- paste0('updated.', fill.in.vs)

# this doesn't work how I want it to:
dt[, (update.names) := mapply(identical,
                              .SD,
                              fixed.vnames,
                              SIMPLIFY = FALSE, USE.NAMES = FALSE),
   .SDcols = fill.in.vs]

dt[,c('var1', 'fixed.var1', 'updated.var1')]
    var1 fixed.var1 updated.var1
 1:    1          1        FALSE
 2:    1          1        FALSE
 3:   NA          1        FALSE
 4:    1          1        FALSE
 5:   NA          1        FALSE
 6:   NA         NA        FALSE
 7:    5          5        FALSE
 8:    5          5        FALSE
 9:   NA         NA        FALSE
10:    5          5        FALSE

我不确定问题出在哪里,但我猜这与我试图让 mapply 将“固定”变量列表识别为要迭代的变量有关。

如有任何建议,我们将不胜感激。

您可以编写一个自定义函数来处理这个问题,并使用 Map 为每一对应用该函数。

library(data.table)

new_cols <- function(x, y) {
  z <- x != y | is.na(x) & is.na(y)
  z[is.na(z)] <- TRUE
  z
}

update.names <- paste0('updated.', fill.in.vs)

dt[, (update.names) := Map(new_cols, dt[, ..fixed.vnames], dt[, ..fill.in.vs])]
dt

#    id var1 var2 year finalyr fixed.var1 fixed.var2 updated.var1 updated.var2
# 1:  a    1   hi 2005    2009          1         hi        FALSE        FALSE
# 2:  a    1   hi 2006    2009          1         hi        FALSE        FALSE
# 3:  a   NA <NA> 2007    2009          1         hi         TRUE         TRUE
# 4:  a    1   hi 2008    2009          1         hi        FALSE        FALSE
# 5:  a   NA   hi 2009    2009          1         hi         TRUE        FALSE
# 6:  b   NA  bye 1995    2002         NA        bye         TRUE        FALSE
# 7:  b    5 <NA> 1996    2002          5        bye        FALSE         TRUE
# 8:  b    5  bye 1997    2002          5        bye        FALSE        FALSE
# 9:  b   NA  bye 1998    2002         NA        bye         TRUE        FALSE
#10:  b    5 <NA> 2002    2002          5       <NA>        FALSE         TRUE

您可能会在控制台中收到警告,请暂时忽略它们。关于它有一个悬而未决的问题。 https://github.com/Rdatatable/data.table/issues/2988