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用法存在什么问题?该如何修复?
解答
问题原因
values[1]的错误使用:values是testAgg的一列,每个元素是子data.table。values[1]取的是整列的第一个元素,而非遍历每一行的values元素,导致mapply只处理了一行数据,而非所有行。- 参数类型不匹配:
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
相关产品推荐
相关产品推荐

