在函数内使用ddply时未发现对象错误



这确实挑战了我调试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甚至在函数内部定义!

我以为这个问题的答案,循环创建新变量,可能会对我有帮助,但是我今天已经做了足够多的头痛,现在是时候举手寻求帮助了

今天这个问题的解决方案是把summarize变成here(summarize)。例如

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中,用于捕获当前上下文。

您可以使用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

我偶尔会遇到这样的问题,当将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所带来的评估问题的头痛。当然,这并不是说Hadley不会带着解决方案出现…

问题在于plyr包本身的代码。在总结函数中,有一行eval(substitute(...),.data,parent.frame())。众所周知,parent.frame()可以做一些非常古怪和意想不到的事情。T

@James的解决方案是一个非常好的解决方案,但是如果我没记错的话@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

看来你有环境问题。全局赋值解决了这个问题,但付出了灵魂的代价:

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拔出我的。从前面的环境中解析,我们可以更接近,但仍然必须将currenv作为全局变量传递:

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()策略都失败了。

最新更新