You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R中sapply运行自定义函数时首个ID全为NA返回错误结果

R自定义面板缺失值填充函数异常问题修复

问题现象

编写了按规则填充面板数据缺失值的自定义函数,仅当首个ID分组的目标变量全为NA时,全表生成的填充变量全部变为NA,其余场景运行正常。

基础依赖与示例数据

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))

自定义填充函数

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)
  
  # 非缺失值直接保留
  try1 <- ifelse(test = !is.na(var),
                 yes = var,
                 no = NA)
  
  # 前后观测值一致、间隔不超过2年的缺失值填充
  try2 <- ifelse(test = is.na(try1) & leadv == lagv &
                        abs(leadyr-lagyr) <= 3 &
                        !is.na(leadv),
                 yes = leadv,
                 no = try1)
  
  # 最后一年观测缺失的特殊填充规则
  ifelse(test = is.na(try2) & yr == finyr &
                abs(yr-lagyr) <= 3 & !is.na(lagv),
         yes = lagv,
         no = try2)
}

正常运行流程

setDT(dt)
# 按ID分组生成最大年份标识
dt[, finalyr := max(year), by = id]
# 转为字符型避免因子问题
dt$var1 <- as.character(dt$var1)
# 空字符串转NA
dt[, var1 := na_if(var1, '')]

fill.in.vs <- c('var1')
fixed.vnames <- paste0('fixed.', fill.in.vs)

# 按ID分组调用填充函数
dt[, (fixed.vnames) := sapply(.SD,
                              FUN = fill.in,
                              year,
                              finalyr,
                              simplify = FALSE, USE.NAMES = FALSE),
   by = id, .SDcols = fill.in.vs]

正常运行结果符合预期,仅符合规则的缺失值被填充。

异常复现场景

当首个ID分组的var1全为空字符串,转NA后运行上述代码,全表fixed.var1全部变为NA:

# 异常测试数据,id=a的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))

已验证的规律

  • 若不将空字符串转为NA,即使首个ID全为空字符串也不会出现问题
  • 只有首个ID分组全为NA时才会触发该问题,若第二个及之后的ID分组全为NA则结果正常
  • 空字符串转NA的方式不影响结果,无论用na_if还是ifelse都会出现该问题

问题原因

R中默认的NA为逻辑类型,你的函数中ifelse返回值在首个分组全为NA时,返回的向量类型为逻辑型。data.table在创建新列时会以首个分组的返回值类型作为整列的类型,后续分组返回的字符型填充值会被强制转换为逻辑型,非逻辑值会被转为NA,最终导致全列异常。

修复方案

将函数中所有显式使用的NA替换为字符型缺失值NA_character_,保证所有分组的返回值类型统一为字符型即可:

# 修复后的填充函数
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)
  
  try1 <- ifelse(test = !is.na(var),
                 yes = var,
                 no = NA_character_)
  
  try2 <- ifelse(test = is.na(try1) & leadv == lagv &
                        abs(leadyr-lagyr) <= 3 &
                        !is.na(leadv),
                 yes = leadv,
                 no = try1)
  
  ifelse(test = is.na(try2) & yr == finyr &
                abs(yr-lagyr) <= 3 & !is.na(lagv),
         yes = lagv,
         no = try2)
}

内容的提问来源于stack exchange,提问作者dmcd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 15:09:04