R中使用mapply时subset参数的非标准求值问题
在mapply中正确使用xtabs/aggregate的subset参数
问题1:如何正确在mapply中使用subset参数
核心问题是mapply会提前对subset的表达式求值,而此时breaks仅存在于warpbreaks数据框中,不在mapply的调用环境里。我们需要延迟表达式的求值,让它在data的环境中解析。这里有两种可靠的解决方式:
方式1:用匿名函数包裹+传递未求值表达式
通过quote()传递未求值的subset条件,然后在匿名函数内部,利用eval()在warpbreaks的环境中解析表达式:
# xtabs的正确调用 mapply(FUN = function(form, sub_expr) { xtabs(formula = form, subset = eval(sub_expr, envir = warpbreaks), data = warpbreaks) }, formula = list(~ wool, ~ wool + tension), subset = list(quote(breaks < 15), quote(breaks < 20))) # aggregate的正确调用 mapply(FUN = function(form, sub_expr) { aggregate(formula = form, subset = eval(sub_expr, envir = warpbreaks), data = warpbreaks, FUN = length) }, formula = list(breaks ~ wool, breaks ~ wool + tension), subset = list(quote(breaks < 15), quote(breaks < 20)))
方式2:提取可变参数(更简洁)
如果你的subset条件是类似“小于某个阈值”的规则,可以把阈值作为独立参数传递,在匿名函数里直接写subset逻辑——此时xtabs/aggregate会自动在data环境中查找breaks:
# xtabs的简化写法 mapply(FUN = function(form, threshold) { xtabs(formula = form, subset = breaks < threshold, data = warpbreaks) }, formula = list(~ wool, ~ wool + tension), threshold = c(15, 20)) # aggregate的简化写法 mapply(FUN = function(form, threshold) { aggregate(formula = form, subset = breaks < threshold, data = warpbreaks, FUN = length) }, formula = list(breaks ~ wool, breaks ~ wool + tension), threshold = c(15, 20))
问题2:为什么formula能正常工作,subset却不行?
这是因为R中公式对象有特殊的变量解析规则:
- 当你创建
~ wool这样的公式时,它会附带一个环境属性(默认是创建它的环境,比如全局环境)。但xtabs/aggregate这类函数在处理公式时,会优先在data参数提供的数据框中查找变量,而不是公式的附带环境,所以wool能被正确找到。 - 而
subset参数不是公式,它是一个逻辑向量或表达式。当你在mapply中写subset = list(breaks < 15, ...)时,mapply会立即在自己的调用环境(全局环境)中尝试求值这个表达式——但全局环境中没有breaks对象(breaks只存在于warpbreaks里),所以就抛出了object 'breaks' not found的错误。
简单总结:公式有“优先从data找变量”的特殊待遇,而subset的表达式在mapply传参阶段就被错误地在全局环境求值了。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

