这确实挑战了我调试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.call
和call
的组合来在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
我偶尔会遇到这样的问题,当将ddply
与summarize
或transform
或其他东西结合在一起时,由于不够聪明,无法预测导航各种环境的细节,我倾向于通过简单地不使用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)
显然,"手动"做这些事情是有代价的,但它通常避免了处理合并ddply
和summarize
所带来的评估问题的头痛。当然,这并不是说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()
策略都失败了。