Errore non trovato nell’object con ddply all’interno di una funzione

Questo ha davvero sfidato la mia capacità di eseguire il debug del codice R.

Voglio usare ddply() per applicare le stesse funzioni a diverse colonne che vengono denominate in modo sequenziale; per esempio. a, b, c. Per fare questo, intendo passare ripetutamente il nome della colonna come una stringa e utilizzare eval(parse(text=ColName)) per consentire alla funzione di eval(parse(text=ColName)) riferimento. Ho afferrato questa tecnica da un’altra risposta.

E questo funziona bene, finché non metto ddply() all’interno di un’altra funzione. Ecco il codice di esempio:

 # 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 

Qualche idea? NewColName è persino definito all’interno della funzione!

Ho pensato che la risposta a questa domanda, loop-to-create-new-variables-in-ddply , potrebbe aiutarmi ma ho fatto abbastanza head banging per oggi ed è ora di alzare la mano e chiedere aiuto.

Puoi farlo con una combinazione di do.call e call per build la chiamata in un ambiente in cui NewColName è ancora visibile:

 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(df,sv) b Ave 1 0 1.5 2 1 3.5 

La soluzione odierna a questa domanda è di summarize here(summarize) . per esempio

 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) , aggiunto a plyr nel dicembre 2012, acquisisce il contesto attuale.

Occasionalmente mi ddply in problemi come questo quando si combinano ddply con summarize o transform o qualcosa del genere e, non essendo abbastanza intelligente da ddply i dettagli della navigazione in vari ambienti, tendo ad affrontare il problema semplicemente non usando il summarize e usando invece il mio funzione anonima:

 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) 

Ovviamente, c'è un costo per fare queste cose 'manualmente', ma spesso evita il mal di testa di affrontare i problemi di valutazione che derivano dalla combinazione di ddply e summarize . Questo non vuol dire, ovviamente, che Hadley non si presenterà con una soluzione ...

Il problema sta nel codice del pacchetto plyr stesso. Nella funzione di riepilogo, esiste una eval(substitute(...),.data,parent.frame()) riga eval(substitute(...),.data,parent.frame()) . È risaputo che parent.frame () può fare cose piuttosto strane e inaspettate. T

La soluzione di @James è una soluzione molto buona, ma se ricordo bene lo stesso @Hadley ha detto prima che il pacchetto plyr non era destinato ad essere utilizzato all’interno delle funzioni.

Scusa, ho sbagliato qui. È noto però che per il momento il pacchetto plyr dà problemi in queste situazioni.

Quindi, ti do una soluzione di base per il problema:

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

Sembra che tu abbia un problema con l’ambiente. L’incarico globale risolve il problema, ma a costo dell’anima:

 library(plyr) 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") ColName = "a" ddply(df, 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=df,y=sv) 

eval sta cercando in parent.frame (1). Quindi se si definisce NewColName al di fuori di MyFunction dovrebbe funzionare:

 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=df,y=sv) 

Usando get per estrarre my.parse dall'ambiente precedente, possiamo avvicinarci molto, ma dobbiamo comunque passare a curenv come globale:

 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=df,y=sv) expression(a)  b Ave 1 0 1.5 2 1 3.5 

Sospetto che ddply stia già valutando in .GlobalEnv, ed è per questo che tutte le parent.frame() e sys.frame() ho provato hanno fallito.