これは、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 が役立つかもしれないと思いましたが、今日は十分なヘッドバンギングを行ったので、手を挙げて助けを求める。
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
この質問に対する今日の解決策は、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)
}
_
2012年12月にplyrに追加されたhere(f)
は、現在のコンテキストをキャプチャします。
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
の組み合わせに起因する評価の問題に対処するという頭痛の種を回避できることがよくあります。もちろん、それはハドリーが解決策を提示しないということではありません...
問題は、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
を使用して以前の環境から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()
戦略はすべて失敗しました。