将函数应用于异构数据行。

时间:2016-09-08 15:23:03

标签: r

假设我有一些(非向量化)函数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.callfoo应用于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包含名为barbaz和{{1}的元素};它并不关心它可能具有的任何其他元素。此外,可以将frobozz直接应用于foo_wrapper的行,而无需诉诸df

do.call

不幸的是,> foo_wrapper(df[4, , drop = TRUE]) [1] 4 也失败了apply

foo_wrapper

1 个答案:

答案 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