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

如何在R的data.table中动态分配变量名并获取NA的起止时间?

问题:在data.table中按分组获取各化学物质NA的起止时间

数据背景

有一份人员执行任务时的化学物质浓度观测数据:3名人员,每人执行2项任务,每项任务重复2次;在5个时间点同时测量3种化学物质的浓度,部分数据存在缺失。数据构造代码如下:

test_dt <- data.table(person = rep(LETTERS[1:3],each = 20),
                      task = rep(LETTERS[24:25], each = 10),
                      reps = rep(c(1,2),each = 5),
                      time = 1:5, chem1 = rnorm(60,1,0.2),
                      chem2 = rnorm(60,4,1.1),chem3 = rnorm(60,2,0.75))
test_dt[person == "A" & reps == 2,`:=`(chem3 = NA_real_)]
test_dt[person == "B" & task == "X" & reps == 1 & time %in% 3:5,chem1 := NA_real_]
test_dt[person == "C" & task == "Y" & reps == 2 & time %in% 3:4,chem2 := NA_real_]

需求

按person、task、reps分组,获取每种化学物质数据首次出现NA的时间和NA结束的时间。

错误尝试及报错

尝试了以下代码:

lapply(c("chem1","chem2","chem3"),function(var){
  start_var = paste0("na_start_",var)
  end_var = paste0("na_end_",var)
  test_dt[is.na(get(var)), 
          .(deparse(substitute(start_var)) = min(time),
            deparse(substitute(end_var)) = max(time)),
          .(person,task,reps)]
})

出现报错:

"  test_dt[is.na(get(var)), 
          .(deparse(substitute(start_var)) ="
>             deparse(substitute(end_var)) = max(time)),
Error: unexpected ')' in "            deparse(substitute(end_var)) = max(time))"
>           .(person,task,reps)]
Error: unexpected ']' in "          .(person,task,reps)]"
> })
Error: unexpected '}' in "}"

解决方案

原代码的问题在于动态命名列的方式错误——deparse(substitute())不能直接用于data.table的j表达式中作为列名,以下是两种可行的解决方法:

方法1:用setNames重命名结果列

先计算NA的起止时间,再对结果列进行重命名:

lapply(c("chem1","chem2","chem3"), function(var){
  start_var <- paste0("na_start_", var)
  end_var <- paste0("na_end_", var)
  # 筛选有NA的行,按分组计算起止时间
  res <- test_dt[is.na(get(var)), 
                 .(min_time = min(time), max_time = max(time)),
                 by = .(person, task, reps)]
  # 重命名列
  setNames(res, c("person", "task", "reps", start_var, end_var))
})

方法2:在j表达式中直接生成动态命名列

结合eval和as.name,在分组计算时直接生成目标列名:

lapply(c("chem1","chem2","chem3"), function(var){
  start_var <- paste0("na_start_", var)
  end_var <- paste0("na_end_", var)
  test_dt[is.na(get(var)), 
          .(
            eval(as.name(start_var)) := min(time),
            eval(as.name(end_var)) := max(time)
          ),
          by = .(person, task, reps)]
})

可选:合并所有结果到一个表

如果需要将三种化学物质的NA起止时间合并为一个data.table,可以用Reduce和merge实现:

all_res <- Reduce(function(x, y) merge(x, y, by = c("person", "task", "reps"), all = TRUE),
                  lapply(c("chem1","chem2","chem3"), function(var){
                    start_var <- paste0("na_start_", var)
                    end_var <- paste0("na_end_", var)
                    res <- test_dt[is.na(get(var)), 
                                   .(min_time = min(time), max_time = max(time)),
                                   by = .(person, task, reps)]
                    setNames(res, c("person", "task", "reps", start_var, end_var))
                  }))
# 查看合并后的结果
print(all_res)

扩展:保留所有分组(包括无NA的)

如果需要保留所有分组,无NA的分组起止时间设为NA,可以去掉is.na(get(var))的筛选,改为在j中判断:

lapply(c("chem1","chem2","chem3"), function(var){
  start_var <- paste0("na_start_", var)
  end_var <- paste0("na_end_", var)
  test_dt[, 
          .(
            eval(as.name(start_var)) := if(any(is.na(get(var)))) min(time[is.na(get(var))]) else NA_integer_,
            eval(as.name(end_var)) := if(any(is.na(get(var)))) max(time[is.na(get(var))]) else NA_integer_
          ),
          by = .(person, task, reps)]
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 11:21:03