运行 当第一个 ID 全部丢失时,sapply 中的函数返回错误结果

Running a function in sapply is returning faulty results when first ID is all missing

我有一个非常奇怪的具体问题,我正在努力解决 google,所以我希望我可以向别人展示。

我编写了一个函数,可以根据一些条件填充一些缺失的数据。例如,对于这样的面板数据:

library(tidyverse)
library(data.table)

dt <- data.frame(id =  c(rep('a', 5),
                         rep('b', 5),
                         rep('c', 5)),
                 var1 = c(rep('', 4), 'bonjour',
                          'bye', NA, 'bye', 'bye', NA,
                          'hi', 'hi', NA, 'hi', 'hi'),
                 year = c(2005:2009,
                          1995:1998, 2002,
                          1995:1999))
dt
    id    var1 year
 1:  a         2005
 2:  a         2006
 3:  a         2007
 4:  a         2008
 5:  a bonjour 2009
 6:  b     bye 1995
 7:  b    <NA> 1996
 8:  b     bye 1997
 9:  b     bye 1998
10:  b    <NA> 2002
11:  c      hi 1995
12:  c      hi 1996
13:  c    <NA> 1997
14:  c      hi 1998
15:  c      hi 1999

我使用以下函数来更新一些缺失值:

fill.in <- function(var, yr, finyr) {
  leadv <- lead(var, n=1, order_by = yr)
  lagv <- lag(var, n=1, order_by = yr)
  
  leadyr <- lead(yr, n=1, order_by = yr)
  lagyr <- lag(yr, n=1, 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)
}

经过一些设置,总的来说我得到了不错的结果:

# ------------ Set-up
# real data is big so use data.table
setDT(dt)

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

# don't want to fill in factor values
dt$var1 <- as.character(dt$var1)

# make empty strings NAs
dt[, var1 := na_if(var1, '')]

# useful for when i'm filling in many variables
fill.in.vs <- c('var1')
fixed.vnames <- paste0('fixed.', fill.in.vs)

# ------------ Call the function and results
dt[, (fixed.vnames) := sapply(.SD,
                              FUN = fill.in,
                              year,
                              finalyr,
                              simplify = FALSE, USE.NAMES = FALSE),
   by = id, .SDcols = fill.in.vs]

# this gives me what I want:
dt
    id    var1 year finalyr fixed.var1
 1:  a    <NA> 2005    2009       <NA>
 2:  a    <NA> 2006    2009       <NA>
 3:  a    <NA> 2007    2009       <NA>
 4:  a    <NA> 2008    2009       <NA>
 5:  a bonjour 2009    2009    bonjour
 6:  b     bye 1995    2002        bye
 7:  b    <NA> 1996    2002        bye
 8:  b     bye 1997    2002        bye
 9:  b     bye 1998    2002        bye
10:  b    <NA> 2002    2002       <NA>
11:  c      hi 1995    1999         hi
12:  c      hi 1996    1999         hi
13:  c    <NA> 1997    1999         hi
14:  c      hi 1998    1999         hi
15:  c      hi 1999    1999         hi

问题是当第一组 ID——例如所有 'a' 值——有空字符串,我将其转换为 NAs,“固定”变量的 all 值最终为 NAs还有。

所以使用相同的代码但使用以下数据,我在新变量中得到了所有 NAs:

# id of 'a' now is all empty strings in var1:
dt <- data.frame(id =  c(rep('a', 5),
                         rep('b', 5),
                         rep('c', 5)),
                 var1 = c(rep('', 5),
                          'bye', NA, 'bye', 'bye', NA,
                          'hi', 'hi', NA, 'hi', 'hi'),
                 year = c(2005:2009,
                          1995:1998, 2002,
                          1995:1999))

# which results in this final data after running the same code above:
dt
    id var1 year finalyr fixed.var1
 1:  a <NA> 2005    2009         NA
 2:  a <NA> 2006    2009         NA
 3:  a <NA> 2007    2009         NA
 4:  a <NA> 2008    2009         NA
 5:  a <NA> 2009    2009         NA
 6:  b  bye 1995    2002         NA
 7:  b <NA> 1996    2002         NA
 8:  b  bye 1997    2002         NA
 9:  b  bye 1998    2002         NA
10:  b <NA> 2002    2002         NA
11:  c   hi 1995    1999         NA
12:  c   hi 1996    1999         NA
13:  c <NA> 1997    1999         NA
14:  c   hi 1998    1999         NA
15:  c   hi 1999    1999         NA

为简洁起见,我不会向您展示我尝试过的所有内容,但会展示一些关于何时发生的观察结果:

  1. 如果我不将空字符串转换为 NA,第一个 ID 中的所有空字符串都不是问题。
  2. 如果 第一个 ID 全部为空字符串,我只会得到这个结果;如果是第二盘,结果还可以。
  3. 我如何将 "" 转换为 NA 并不重要,也就是说,这不是 na_if 的问题,因为当我使用 ifelse.[= 时也会发生这种情况46=]

总的来说,我对正在发生的事情或如何进一步调查它感到很困惑。如果有任何帮助,我将不胜感激。

当我运行你的代码时,我收到这个警告:

1: In [.data.table(dt, , :=((fixed.vnames), sapply(.SD, FUN = fill.in, : Coercing 'character' RHS to 'logical' to match the type of the target column (column 0 named '').

我得到了两次,一次是第二组,一次是第三组。 正如它所说,变量 fixed.var1 被初始化为逻辑变量(对于组 id==a);然后将稍后添加的值转换为相同的 class 'logical'.

这里的罪魁祸首是您的函数 fill.in(),因为例如

logicalVar <- fill.in( var=rep(NA,5), yr=2005:2009, finyr=rep(2009,5)); class(logicalVar)

returns 是一个逻辑变量。 因此,您需要做的就是将 as.character() 包裹在函数的 return 周围。