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

R中data.table使用mapply计算置信区间出错,求解决方法

问题:data.table行应用自定义函数失败,mapply用法错误排查

我想对data.table的每一行应用自定义函数,先构造测试数据:

set.seed(13579) 
cat1N <- 10
cat2N <- 15
cat3N <- 7
group = c(rep("Group1", cat1N), rep("Group1", cat1N), rep("Group1", cat1N), 
          rep("Group2", cat2N), rep("Group2", cat3N)) # policyID
year = c(rep(2015, cat1N), rep(2016, cat1N), rep(2017, cat1N), 
         rep(2016, cat2N), 
         rep(2017, cat3N))
category = c(rep("cat1", cat1N/2), rep("cat2", cat1N/2), rep("cat1", cat1N/2), rep("cat2", cat1N/2),  rep("cat1", cat1N/2), rep("cat2", cat1N/2), 
             rep("cat2", 7), rep("cat3", 8), rep("cat3", 3), rep("cat1", 4)) # plan 
value = c(abs(rnorm(cat1N)*100), abs(rnorm(cat1N)*100), abs(rnorm(cat1N)*100), 
          abs(rnorm(cat2N)*100), abs(rnorm(cat3N)*100))

require("data.table")

testData <- data.table(group = group, 
                       year = year, 
                       category = category, 
                       value = value)

接着按分组聚合数据:

cohort = c("group" ,"category", "year")
testAgg <- testData[, group := group][, .(values = .(.SD)), by = cohort] 

聚合后的数据结构:

group category year            values
 1: Group1     cat1 2015 <data.table[5x1]>
 2: Group1     cat2 2015 <data.table[5x1]>
 3: Group1     cat1 2016 <data.table[5x1]>
 4: Group1     cat2 2016 <data.table[5x1]>
 5: Group1     cat1 2017 <data.table[5x1]>
 6: Group1     cat2 2017 <data.table[5x1]>
 7: Group2     cat2 2016 <data.table[7x1]>
 8: Group2     cat3 2016 <data.table[8x1]>
 9: Group2     cat3 2017 <data.table[3x1]>
10: Group2     cat1 2017 <data.table[4x1]>

然后定义计算置信区间的函数,尝试用mapply对每一行应用:

calculateCI <- function(value){
  
  avg <- mean(value)
  s <- sqrt(var(value))
  n <- length(value)
  
  error <- qnorm(0.975)*s/sqrt(n)
  
  lower <- avg - error
  upper <- avg + error 
  
  return(c(lower, upper)) 
} 

testAgg[, 'lowerCI' := mapply(calculateCI, values[1])[1]]
# 警告信息:In mean.default(value) : argument is not numeric or logical: returning NA
testAgg[, 'upperCI' := mapply(calculateCI, values[1])[2]]
# 警告信息:In mean.default(value) : argument is not numeric or logical: returning NA

执行时出现警告,无法正确计算置信区间,请问mapply用法存在什么问题?该如何修复?


解答

问题原因

  1. values[1]的错误使用:values是testAgg的一列,每个元素是子data.table。values[1]取的是整列的第一个元素,而非遍历每一行的values元素,导致mapply只处理了一行数据,而非所有行。
  2. 参数类型不匹配:calculateCI期望接收数值向量,但你传递的是子data.table(.SD包含所有列,即使只有value列,仍为data.table类型),mean()无法直接处理data.table,因此返回NA并触发警告。

修复方案

方案一:修改聚合逻辑,存储数值向量而非子data.table

聚合时直接提取value列的向量,简化后续处理:

# 重新聚合,存储value向量而非子data.table
testAgg <- testData[, .(values = list(value)), by = cohort]

# 应用函数并拆分结果为lowerCI和upperCI
ci_results <- t(mapply(calculateCI, testAgg$values))
testAgg[, c("lowerCI", "upperCI") := .(ci_results[,1], ci_results[,2])]

方案二:适配函数参数,处理子data.table输入

如果要保留子data.table结构,修改函数先提取数值列:

calculateCI <- function(dt){
  value <- dt$value # 从子data.table中提取数值向量
  avg <- mean(value)
  s <- sqrt(var(value))
  n <- length(value)
  
  error <- qnorm(0.975)*s/sqrt(n)
  
  return(c(lower = avg - error, upper = avg + error)) 
}

# 正确遍历values列的每一个子data.table
ci_results <- t(mapply(calculateCI, testAgg$values))
testAgg[, c("lowerCI", "upperCI") := .(ci_results[,1], ci_results[,2])]

方案三:data.table原生风格写法

用lapply结合by=.I按行处理,更贴合data.table的使用习惯:

testAgg[, c("lowerCI", "upperCI") := {
  # 对每一行的values子data.table计算CI
  ci <- lapply(values, function(dt){
    val <- dt$value
    avg <- mean(val)
    se <- sqrt(var(val)/length(val))
    error <- qnorm(0.975)*se
    list(lower = avg - error, upper = avg + error)
  })
  # 拆分list为两列
  data.table::rbindlist(ci)
}, by = .I] # by=.I表示按每一行处理

验证结果

执行任意方案后,testAgg会新增lowerCI和upperCI列,无警告且数值正确:

group category year            values   lowerCI   upperCI
 1: Group1     cat1 2015 <data.table[5x1]>  40.32075 134.50944
 2: Group1     cat2 2015 <data.table[5x1]>  38.79767 142.17555
 ...(其余行省略)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:27:21