R语言计算单条观测对整体结果的影响及循环问题排查
问题描述
我有一个包含物品数量、实际成本和预测成本的概览表:
myData <- data.table("itemCount" = c(3000, 20, 50, 9), "cost" = c(120, 118, 165, 93), "prediction" = c(120, 100, 150, 120))
随后计算了单个观测值和整体的利润:
myData[, "profit" := cost/prediction] total <- myData[, .(itemsTotal = sum(itemCount), costTotal = sum(cost), predictionTotal = sum(prediction))][ , "profit" := costTotal/predictionTotal ]
现在需要计算:排除每一行观测值后,整体利润会是多少。比如排除第二行的示例代码:
myData$diffinProfit <- NA myDataEx <- myData[- 2, ] totalEx <- myDataEx[, .(itemsTotal = sum(itemCount), costTotal = sum(cost), predictionTotal = sum(prediction))][ , "profit" := costTotal/predictionTotal ]
我写了一个for循环来实现,但只得到第一个观测值对应的结果:
myData$diffinProfit <- NA for(observation in seq_along(length(myData)-1)){ myDataEx <- myData[- observation, ] totalEx <- myDataEx[, .(itemsTotal = sum(itemCount), costTotal = sum(cost), predictionTotal = sum(prediction))][ , "profit" := costTotal/predictionTotal ] myData$diffinProfit[[observation]] <- totalEx$profit }
想知道怎么修复这个循环,以及能不能用apply类函数、mapply或purrr函数实现需求?
解决方案
1. 修复for循环
循环失效的核心问题是迭代范围错误:length(myData)返回的是数据表的列数(这里是4),seq_along(length(myData)-1)只会生成[1,2,3],漏掉了第4行的计算。我们需要遍历的是数据表的行索引,即从1到nrow(myData)。
修改后的循环代码:
myData$diffinProfit <- NA # 遍历所有行的索引 for(observation in seq_len(nrow(myData))){ myDataEx <- myData[- observation, ] totalEx <- myDataEx[, .(costTotal = sum(cost), predictionTotal = sum(prediction))][ , profit := costTotal/predictionTotal ] myData$diffinProfit[observation] <- totalEx$profit }
优化版循环
提前计算全局总成本和总预测值,避免每次循环重复求和,效率更高:
total_cost <- sum(myData$cost) total_pred <- sum(myData$prediction) myData$diffinProfit <- NA for(observation in seq_len(nrow(myData))){ # 直接计算排除当前行后的利润 ex_cost <- total_cost - myData$cost[observation] ex_pred <- total_pred - myData$prediction[observation] myData$diffinProfit[observation] <- ex_cost / ex_pred }
2. 使用apply类/向量化方法实现
base R的sapply
用sapply遍历行索引,一行完成计算:
total_cost <- sum(myData$cost) total_pred <- sum(myData$prediction) myData$diffinProfit <- sapply(seq_len(nrow(myData)), function(i){ (total_cost - myData$cost[i]) / (total_pred - myData$prediction[i]) })
purrr的map_dbl(tidyverse风格)
如果习惯tidyverse生态,用purrr::map_dbl更简洁:
library(purrr) total_cost <- sum(myData$cost) total_pred <- sum(myData$prediction) myData$diffinProfit <- map_dbl(seq_len(nrow(myData)), ~{ (total_cost - myData$cost[.x]) / (total_pred - myData$prediction[.x]) })
data.table内置高效方法
利用data.table的分组计算特性,直接按行生成结果,无需循环:
# 计算全局总成本和总预测值,添加为临时列 myData[, total_cost := sum(cost)] myData[, total_pred := sum(prediction)] # 直接计算排除当前行后的利润 myData[, diffinProfit := (total_cost - cost) / (total_pred - prediction)] # 可选:删除临时列 myData[, c("total_cost", "total_pred") := NULL]
内容的提问来源于stack exchange,提问作者Nneka
相关产品推荐
相关产品推荐

