假设我有一些(非向量化)函数foo
:
foo <- function (bar, baz, frobozz, frotz = 42) {
if (frobozz) {
frotz
}
else {
bar * nchar(baz)
}
}
毫无疑问,这是一个愚蠢的功能,但出于这个问题的目的,请考虑一下。 (IOW,以修改foo
为基础的答案超出范围。)
另外,假设我有data.frame
df
,如下所示:
> df
frobozz bar baz
1 TRUE 1 a
2 FALSE 2 b
3 TRUE 3 c
4 FALSE 4 d
5 TRUE 5 e
现在,df
的每一行都可以被视为异类命名列表(我将在此后缩写为 record )。
事实上,将df
行中的任何一行投射为这样的记录并不困难:
> df[1, , drop = TRUE]
$frobozz
[1] TRUE
$bar
[1] 1
$baz
[1] "a"
此外,任何命名槽的记录中的值都适合作为foo
签名中同名参数的类型。
这意味着我可以使用do.call
将foo
应用于df
的任何单行:
> do.call(foo, df[1, , drop = TRUE])
[1] 42
> do.call(foo, df[2, , drop = TRUE])
[1] 2
(请注意,即使df
列的排序和foo
所需参数的排序不匹配,这仍然有效。)
现在,我想通过将foo
应用于 df
的每一行来创建一个新列。
我原本希望apply
可以完成任务,但它失败了:
> apply(df, 1, foo)
Error in FUN(newX[, i], ...) :
argument "frobozz" is missing, with no default
当然,我可以诉诸这样的事情:
sapply(1:nrow(df), function (i) { do.call(foo, df[i, , drop = TRUE]) })
是否有一种不那么无知的方式来实现这一目标?
这个问题的变体可能更容易处理。
考虑函数foo_wrapper
:
foo_wrapper <- function ( record ) {
foo( record$bar, record$baz, record$frobozz )
}
此功能比foo
更灵活,因为它所要求的是它的参数record
包含名为bar
,baz
和{{1}的元素};它并不关心它可能具有的任何其他元素。此外,可以将frobozz
直接应用于foo_wrapper
的行,而无需诉诸df
:
do.call
不幸的是,> foo_wrapper(df[4, , drop = TRUE])
[1] 4
也失败了apply
:
foo_wrapper
答案 0 :(得分:2)
您只需Vectorize
您的功能,然后使用with()
来访问变量。例如,您的样本数据......
dd <- read.table(text="frobozz bar baz
1 TRUE 1 a
2 FALSE 2 b
3 TRUE 3 c
4 FALSE 4 d
5 TRUE 5 e", header=T, stringsAsFactors=F)
然后你可以运行
with(dd, Vectorize(foo)(frobozz=frobozz, bar=bar, baz=baz))
# [1] 42 2 42 4 42