3.3 审讯与排错:错误处理与调试


3.3 审讯与排错:错误处理与调试

本节摘要:批量勘验跑到第 37 个国家突然报错,循环戛然而止——这是自动化时代躲不开的场景。本节讲 R 的三类异常信号(error、warning、message)、tryCatch 护栏写法,以及 traceback、browser、condition 本身的排查动作,让流水线"坏一条不停全线"。

先分清三种信号

  • error:致命,当场停止执行。比如对字符列求均值。
  • warning:可疑但继续跑。比如 na.rm 没设,结果带 NA。
  • message:例行通报,比如包加载信息。

排错的第一课是不要无视 warning——它是 error 的前兆。上一节"空向量倒序循环"那类事故,往往先以 warning 形式露头。

tryCatch:给流水线装护栏

safe_growth <- function(values, years) { tryCatch( growth_rate(values, years), error = function(e) { warning("该国家数据不完整,已跳过:", conditionMessage(e)) NA_real_ # 返回缺失而不是中断 } ) } # 批量跑:个别国家坏了,全线照常收尾 countries <- unique(gapminder_long$country) results <- sapply(countries, function(cty) { d <- gapminder_long[gapminder_long$country == cty, ] safe_growth(d$lifeExp, d$year) }) sum(is.na(results)) # 统计有多少案失败,回头逐个排查

护栏的代价是错误被吞掉。所以失败要记账(warning、日志、NA 标记),事后必须回头清账——只加 tryCatch 不清账,等于把故障埋进结果里。

定位病灶的三件工具

# 第一件:报错后看调用栈,错误从哪一层抛出 traceback() # 第二件:在函数里插断点,执行到这里进入现场 debug_growth <- function(values, years) { browser() # 停在这里,可查看变量 first <- values[1] last <- values[length(values)] (last / first)^(1 / (years[length(years)] - years[1])) - 1 } # 第三件:RStudio 可视化调试——点行号设断点,与 browser 等效

进入 browser 现场后,常用口令:n 下一行、Q 退出、直接敲变量名查看取值。现场查看的重点永远是输入是否符合预期——九成的函数错误是喂进去的数据形状不对。

排错工作流

⚠️ 常见坑:在循环里裸跑会报错的函数,第 37 次迭代失败时,前 36 次的结果也一并丢失。批量处理前先小样本试跑(比如先跑 5 个国家),确认稳定再放全量。

实战复盘:一次批量任务的完整排错过程

把本章工具串成一次真实排错。案情:对全部国家批量计算增速,跑到中段抛出 error。完整过程如下:

# 第一步:裸跑复现,读报错原文 sapply(countries, function(cty) { d <- gapminder_long[gapminder_long$country == cty, ] growth_rate2(d$lifeExp, d$year) }) # 报错:values 与 years 长度不一致:12 对 10 # ——某个国家的年份里有重复登记或缺失 # 第二步:用命名向量精确定位是哪个国家 res <- sapply(countries, function(cty) { tryCatch({ d <- gapminder_long[gapminder_long$country == cty, ] growth_rate2(d$lifeExp, d$year) }, error = function(e) NA_real_) }) bad <- names(res)[is.na(res)] # 失败国家名单,一个不漏 # 第三步:进 browser 现场勘验问题国家的原始行 d_bad <- gapminder_long[gapminder_long$country == bad[1], ] d_bad # 肉眼可见重复年份行 d_bad %>% count(year) %>% filter(n > 1) # 机器确认 # 第四步:修复(去重)后重跑,res 全绿

这段复盘展示的节奏值得固化成习惯:复现 → 定位 → 进现场 → 修复 → 全量重跑。第二步"先包 tryCatch 拿到完整失败名单"是关键一跳——比逐个手跑快,也比直接修第一个发现的问题更稳,因为坏数据常常不止一处。

三件工具的选择依据

工具 用在什么阶段 一句话定位
traceback 报错之后立即看 回答"错在哪一层"
browser 断点 知道哪段代码可疑 回答"运行到这行时变量什么样"
recover 模式 报错现场想逐层挑 options(error = recover) 后报错进选单
tryCatch 修完之后上量产 回答"个别失败如何不影响全线"
小样本试跑 放全量之前 用 5% 的成本暴露 95% 的问题

时序上它们不是并列选项而是流水线:前四件围着一 次故障转,第五件是量产前的例行安检。把顺序用反(比如一上来就 tryCatch 把错误吞掉再瞎猜)是排错中最常见的弯路。

概念辨析:修数据还是修代码

排错的终点站要回答一个判断题:这次故障的病根在数据还是在代码?判据有两条——病发范围(只有个别国家出错,多半是数据;全部一致出错,多半是代码);复现稳定性(同一输入稳定复现,代码问题概率大;换批数据就好了,数据问题概率大)。数据病修数据(去重、补缺、口径统一),代码病修代码(校验、边界、类型),修错了方向会陷入"补丁摞补丁"的泥潭。

本节要点回顾

  • 三信号分级:error 停机、warning 预警、message 通报,warning 不可无视
  • tryCatch 加替代值:坏一条不停全线,失败要记账
  • traceback 看栈、browser 进现场:定位病灶的两件主工具
  • 先查输入形状:函数错误九成出在喂进去的数据
  • 小样本试跑:批量放行前的例行安检

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U