【发布时间】:2017-01-16 14:45:34
【问题描述】:
假设我有一些(非矢量化)函数foo:
foo <- function (bar, baz, frobozz, frotz = 42) {
if (frobozz) {
frotz
}
else {
bar * nchar(baz)
}
}
毫无疑问,这是一个愚蠢的功能,但出于这个问题的目的,将其视为给定的。 (IOW,基于修改 foo 的答案超出范围。)
另外,假设我有data.framedf,如下图:
> 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 和frobozz 的元素;它不关心它可能具有的任何其他元素。此外,可以将foo_wrapper 直接应用到df 的行,而不必求助于do.call:
> foo_wrapper(df[4, , drop = TRUE])
[1] 4
不幸的是,apply 也以 foo_wrapper 失败:
> apply(df, 1, foo_wrapper)
Error in record$frobozz : $ operator is invalid for atomic vectors
【问题讨论】:
-
见
?mapply。试试do.call(mapply,c(foo,df))。 -
我猜this 可能有用。请参阅
?mapply或其包装器?Vectorize--do.call(Vectorize(foo), df) -
@Hack-R
baz列必须是字符才能按foo的顺序工作。 -
@nicola:谢谢!我在发帖之前尝试过
mapply,但毫无头绪:例如mapply(foo, df)和mapply(foo, df[, c("bar", "baz", "frobozz")]),导致出现的错误类似于我在使用apply时遇到的错误。一百万年后,我不会想到通过do.call做到这一点。它当然工作得很好,但我仍然对这个结构有点迷惑。您介意发表您的评论及其背后的原因吗?
标签: r