使用purrr结合mapply捕获错误:代码实现是否正确?
safely()与mapply()实现带错误捕获的元素级求和? 我来帮你梳理清楚这个问题~首先看你给出的代码,safe_sum的定义没写完,咱们先把它补全,再一步步分析你的实现思路是否正确,以及如何得到符合预期的输出。
先回顾你的原始测试代码:
x.good <- c(2, 2, 3, 3) x.bad <- c(2, 2, "A", 3) y <- c(2, 2, 3, 3) mapply(sum, x.good, y) # 运行正常,输出 [1] 4 4 6 6 mapply(sum, x.bad, y) # 报错,因为字符"A"无法和数值相加
这部分的行为是符合预期的——当输入包含非数值元素时,sum会直接抛出错误中断执行。
接下来是核心:用purrr::safely()包装sum来捕获错误。首先补全safe_sum的定义:
library(purrr) # safely()会返回一个包装后的函数,出错时返回otherwise指定的值(这里用NA_real_保证数值类型一致) safe_sum <- safely(sum, otherwise = NA_real_)
关于你的实现思路的正确性
你的方向是对的:用safely()包装易出错的函数,就能在不中断整个流程的前提下捕获错误。但直接用mapply(safe_sum, x.bad, y)的话,输出会是一个嵌套列表,这可能不是你想要的结果——因为safely包装后的函数每次调用都会返回一个包含result(成功时的输出)和error(出错时的错误信息)的列表,mapply会把这些小列表组合成一个大列表。
正确的实现方式
如果你想得到一个和原始mapply输出类似的向量(成功时返回求和值,失败时返回NA),可以用以下两种方式:
方式1:用purrr的map2(更贴合tidyverse风格)
map2是purrr专门用于处理两个输入向量的函数,配合safely使用非常顺畅:
# 调用safe_sum处理每一对x.bad和y的元素,然后提取result部分转为数值向量 map2(x.bad, y, safe_sum) %>% map_dbl("result")
输出结果:
[1] 4 4 NA 6
方式2:坚持用mapply的处理方法
如果一定要用基础的mapply,需要对返回的嵌套列表做一点处理:
# 用mapply调用safe_sum,转置后提取result列 mapply(safe_sum, x.bad, y) %>% t() %>% as.data.frame() %>% pull(result)
同样会得到[1] 4 4 NA 6的结果。这里转置是因为mapply默认会把每个safe_sum的返回结果按列排列,转置后更方便提取result字段。
小总结
你的核心思路(用safely()捕获错误)是完全正确的,只需要注意safely()的返回格式是包含result和error的列表,通过提取result就能得到我们需要的带错误捕获的元素级求和结果。如果是用purrr生态的话,更推荐用map2来替代mapply,代码更简洁直观。
内容的提问来源于stack exchange,提问作者Nick Crouch

