使用transform并调用函数将列添加到数据框 - 这是一种恼人的奇怪行为

时间:2013-04-12 17:04:15

标签: r

可重复和简化的示例来解释我的核心问题

我从一个以向量作为参数的函数开始,例如:fVec <- function(v) v[1]*v[2]

为了进行比较,使用显式参数表达相同功能的直接方法是:fDirect <- function(a, b) a*b

在我的用例中,我想构建一个具有上述fDirect等显式参数的函数,但是通过调用初始函数fVec来实现它。为此,我定义了:fIndirect <- function(a, b) fVec(c(a, b))。 (我在下面解释为什么我想做那些显然没有任何意义的事情!)

正如预期的那样,fVec(c(2, 3))fDirect(2, 3)fIndirect(2, 3)返回6,到目前为止一直很好。

现在为了绘图目的,我使用我想要绘制的数据构建一个数据框,如下所示:

  1. 创建函数参数值:mydf <- data.frame(a=1:3, b=2:4)
  2. 使用transform在新列中添加函数值。
  3. 使用fDirecttransform(mydf, v=fDirect(a, b))按预期工作,返回:

    > transform(mydf, v=fDirect(a, b))
      a b  v
    1 1 2  2
    2 2 3  6
    3 3 4 12
    

    但是,使用fIndirect时,它不会返回所需的函数值:

    > transform(mydf, v=fIndirect(a, b))
      a b v
    1 1 2 2
    2 2 3 2
    3 3 4 2
    

    在调试中,我意识到使用fIndirecttransform传递给fVec一个向量参数是数据框的列ab的并集,即:c(mydf[["a"]], mydf[["b"]])。结果,fVec执行了它编程要执行的操作,即评估前两个元素的乘积,从而为所有行返回1*2=2

    到目前为止,最好的解决方案我能够解决这个transform挑战,使用apply如下:

    cbind(mydf, v=apply(mydf, 1, function(row) fIndirect(row["a"], row["b"])))
    

    问题 的: 为什么transform将两个数据框列都传递给fVecfIndirect,而不是像使用fDirect一样调用它时的行为,其中它在一行中评估函数的一行时间?这是一个R错误还是我误解了R的工作原理,可能是关于范围界定和/或论证投射的事情?

    上下文

    本节解释了为什么我遵循这样一个过程,也许有人可以指出一种更好的方法来构建它。

    我有一个相当复杂的目标函数,我尝试优化(即fVec角色)。这个函数有一个可变数量的参数作为命名向量参数传入,以方便我使用此函数的各种方式,特别是使用期望向量作为目标函数的参数的BBoptim优化器。

    在某些情况下,变量参数的数量为1或2,我想绘制我的目标函数(我在1-dim情况下使用plot;我使用levelplot和{{1} }来自2-dim情况下的网格包。)

    然后,我构建了一个带有显式参数(即wireframe角色)的临时函数,以便于构建我想要绘制成数据帧的数据(即fIndirect角色)。由于我的目标函数相对复杂,我需要一个带矢量参数的版本,我希望通过调用原始目标函数mydf来实现我的临时函数fIndirect

    任何人都可以提出一个更好的方法来实现相同的目标,而不是我在上面的简化示例中展示的过程吗?

1 个答案:

答案 0 :(得分:0)

所以我可以将这个问题标记为已回答,对Joran表示赞赏,关键问题不在transform;问题是fIndirect不支持矢量化操作。

fDirect带有支持向量化操作的向量参数:

fDirect(1:3,2:4)
[1]  2  6 12

fIndirect具有相同的向量参数不支持向量化操作:

fIndirect(1:3,2:4)
[1]  2 

它有意义fIndirect(1:3,2:4) = fVec(c(1:3,2:4)),这是前两个元素的乘积。

我的误解是认为transform(mydf, v=fct(a, b))会在数据框fct的每一行调用函数mydf,即每行索引fct(mydf[i, "a"], mydf[i, "b"]) i每次调用都使用一对标量。我学会了通过将函数的矢量化形式调用为v来生成数据框中的新列fct(mydf[["a"]], mydf[["b"]])。在我自学R的过程中,我错过了这种基本的操作模式。

因此,fIndirect正确实现以支持矢量化操作可能类似于:

fIndirect <- function(a, b)
  vapply(1:length(a), function(row) fVec(c(a[row], b[row])), FUN.VALUE=1)

底线,在实现函数时,确保通过循环遍历向量元素来支持向量参数,因为函数实现中!

关于我的上下文中的整个过程,我想它没关系。如果不需要自动化该过程,作为R中的过程的替代代码,我想也许可以探索并使用诸如Rcmdr之类的UI交互式包来绘制数据。

我希望我学到的东西的细节对某人有用。