在函数内部使用ddply找不到对象错误

时间:2011-08-05 10:50:35

标签: r function scope plyr

这确实挑战了我调试R代码的能力。

我想使用ddply()将相同的函数应用于按顺序命名的不同列;例如。 a,b,c。为此,我打算重复将列名称作为字符串传递,并使用eval(parse(text=ColName))允许函数引用它。我从另一个答案中抓住了这个技术。

这很有效,直到我将ddply()放在另一个函数中。以下是示例代码:

# Required packages:
library(plyr)

myFunction <- function(x, y){
    NewColName = "a"
    z = ddply(x, y, summarize,
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}

a = c(1,2,3,4)
b = c(0,0,1,1)
c = c(5,6,7,8)
df = data.frame(a,b,c)
sv = c("b")

#This works.
ColName = "a"
ddply(df, sv, summarize,
        Ave = mean(eval(parse(text=ColName)), na.rm=TRUE)
)

#This doesn't work
#Produces error: "Error in parse(text = NewColName) : object 'NewColName' not found"
myFunction(df,sv)

#Output in both cases should be
#  b Ave
#1 0 1.5
#2 1 3.5

有什么想法吗?甚至在函数内定义了NewColName!

我认为这个问题的答案loops-to-create-new-variables-in-ddply可能会对我有所帮助,但我已经为今天做了足够的敲击,是时候举手并寻求帮助了。

5 个答案:

答案 0 :(得分:22)

今天这个问题的解决方案是将summarize变为here(summarize)。 e.g。

myFunction <- function(x, y){
    NewColName = "a"
    z = ddply(x, y, here(summarize),
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}

here(f),于2012年12月添加到plyr,捕获了当前的背景。

答案 1 :(得分:14)

您可以使用do.callcall的组合来完成此操作,以便在NewColName仍然可见的环境中构建呼叫:

myFunction <- function(x,y){
NewColName <- "a"
z <- do.call("ddply",list(x, y, summarize, Ave = call("mean",as.symbol(NewColName),na.rm=TRUE)))
return(z)
}

myFunction(d.f,sv)
  b Ave
1 0 1.5
2 1 3.5

答案 2 :(得分:9)

在将ddplysummarizetransform或其他内容结合使用时,我偶尔会遇到这样的问题,并且不够聪明,无法判断导航各种环境的细节我倾向于通过简单地不使用summarize而不是使用我自己的匿名函数来解决问题:

myFunction <- function(x, y){
    NewColName <- "a"
    z <- ddply(x, y, .fun = function(xx,col){
                             c(Ave = mean(xx[,col],na.rm=TRUE))}, 
               NewColName)
    return(z)
}

myFunction(df,sv)

显然,“手动”执行此操作需要付出代价,但这通常可以避免处理ddplysummarize合并后的评估问题。当然,这并不是说哈德利不会出现解决方案......

答案 3 :(得分:4)

问题在于plyr包本身的代码。在汇总函数中,有一行eval(substitute(...),.data,parent.frame())。众所周知,parent.frame()可以做很多时髦和意想不到的事情。 Ť

@pames的解决方案是一个非常好的解决方法,但如果我记得正确@Hadley自己之前说过plyr包不打算在函数中使用。

抱歉,我错了。众所周知,目前,plyr包装在这些情况下会出现问题。

因此,我为您提供了解决问题的基本解决方案:

myFunction <- function(x, y){
    NewColName = "a"
    z = aggregate(x[NewColName],x[y],mean,na.rm=TRUE)
    return(z)
}
> myFunction(df,sv)
  b   a
1 0 1.5
2 1 3.5

答案 4 :(得分:3)

看起来你有环境问题。全球任务解决了这个问题,但却以牺牲一个人的灵魂为代价:

library(plyr)

a = c(1,2,3,4)
b = c(0,0,1,1)
c = c(5,6,7,8)
d.f = data.frame(a,b,c)
sv = c("b")

ColName = "a"
ddply(d.f, sv, summarize,
        Ave = mean(eval(parse(text=ColName)), na.rm=TRUE)
)

myFunction <- function(x, y){
    NewColName <<- "a"
    z = ddply(x, y, summarize,
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}

myFunction(x=d.f,y=sv)

eval正在查找parent.frame(1)。因此,如果你改为在MyFunction之外定义NewColName,它应该工作:

rm(NewColName)
NewColName <- "a"
myFunction <- function(x, y){

    z = ddply(x, y, summarize,
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}
myFunction(x=d.f,y=sv)

通过使用get从早期环境中提取my.parse,我们可以更接近,但仍然必须将curenv作为全局传递:

myFunction <- function(x, y){
    NewColName <- "a"
    my.parse <- parse(text=NewColName)
    print(my.parse)
    curenv <<- environment()
    print(curenv)

    z = ddply(x, y, summarize,
            Ave = mean( eval( get("my.parse" , envir=curenv ) ), na.rm=TRUE)
    )
    return(z)
}

> myFunction(x=d.f,y=sv)
expression(a)
<environment: 0x0275a9b4>
  b Ave
1 0 1.5
2 1 3.5

我怀疑ddply已在.GlobalEnv中进行评估,这就是为什么我尝试的所有parent.frame()sys.frame()策略都失败了。