将函数应用于data.frame的异构行

将函数应用于data.frame的异构行,r,R,假设我有一些(非矢量化的)函数foo: foo <- function (bar, baz, frobozz, frotz = 42) { if (frobozz) { frotz } else { bar * nchar(baz) } } 现在,df的每一行都可以看作是一个异构的命名列表(我将从此缩写为record) 事实上,将df的任何行转换为这样的记录并不困难: > df[1, , drop = TRUE] $f

假设我有一些(非矢量化的)函数
foo

foo <- function (bar, baz, frobozz, frotz = 42) {
    if (frobozz) {
        frotz
    }
    else {
        bar * nchar(baz)
    }
}
现在,
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\u wrapper

foo_wrapper <- function ( record ) {
    foo( record$bar, record$baz, record$frobozz )
}
不幸的是,
apply
foo\u包装器一起失败

> apply(df, 1, foo_wrapper)
Error in record$frobozz : $ operator is invalid for atomic vectors

您可以将函数向量化,然后使用
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)

请参见
?mapply
。试试
do.call(mapply,c(foo,df))
。我想可能有用。请参阅
?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
这样做。它确实工作得很好,但我仍然对它的结构感到有点迷惑。你介意把你的评论和背后的理由作为回答吗?非常优雅+1.
> apply(df, 1, foo_wrapper)
Error in record$frobozz : $ operator is invalid for atomic vectors
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