将函数应用于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-Rbaz
列必须按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