Error de objeto no encontrado con ddply dentro de una función


Esto realmente ha desafiado mi capacidad de depurar código R.

Quiero usar ddply() para aplicar las mismas funciones a diferentes columnas que se nombran secuencialmente; por ejemplo. a, b, c. Para hacer esto pretendo pasar repetidamente el nombre de la columna como una cadena y usar el eval(parse(text=ColName)) para permitir que la función haga referencia a ella. Tomé esta técnica de otra respuesta.

Y esto funciona bien, hasta que pongo ddply() dentro de otra función. Aquí está el código de ejemplo:

# 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

¿Alguna idea? NewColName incluso se define dentro de la función!

Pensé que la respuesta a esta pregunta, loops-to-create-new-variables-in-ddply, podría ayudarme, pero he hecho suficientes golpes de cabeza por hoy y es hora de levantar la mano y pedir ayuda.

Author: Community, 2011-08-05

5 answers

Puede hacer esto con una combinación de do.call y call para construir la llamada en un entorno donde NewColName todavía es visible:

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
 14
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-30 08:54:00

La solución de hoy a esta cuestión es convertir summarize en here(summarize). por ejemplo,

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), añadido a plyr en diciembre de 2012, captura el contexto actual.

 22
Author: Peter O,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-27 18:38:26

De vez en cuando me encuentro con problemas como este al combinar ddply con summarize o transform o algo así y, al no ser lo suficientemente inteligente como para adivinar los entresijos de navegar por varios entornos, tiendo a dejar de lado el problema simplemente al no usar summarize y en su lugar usar mi propia función anónima:

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)

Obviamente, hay un costo para hacer esto 'manualmente', pero a menudo evita el dolor de cabeza de lidiar con los problemas de evaluación que provienen de combinar ddply y summarize. Que no di, por supuesto, que Hadley no aparecerá con una solución...

 9
Author: joran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-05 13:47:21

El problema radica en el código del propio paquete plyr. En la función resumir, hay una línea eval(substitute(...),.data,parent.frame()). Es bien sabido que el padre.frame() puede hacer cosas bastante funky e inesperadas. T

La solución de @James es una solución muy buena, pero si no recuerdo mal, el propio @Hadley dijo antes que el paquete plyr no estaba destinado a ser utilizado dentro de funciones.

Lo siento, me equivoqué aquí. Se sabe sin embargo que por el momento, el paquete plyr da problemas en estas situaciones.

Por lo tanto, les doy una solución básica para el problema :

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
Author: Joris Meys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-07 21:31:44

Parece que tienes un problema de entorno. La asignación global soluciona el problema, pero a costa del alma de uno:

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 está buscando en el padre.marco(1). Así que si en su lugar defines NewColName fuera de myFunction debería funcionar:

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)

Usando get para sacar mi.analizar desde el entorno anterior, podemos acercarnos mucho más, pero todavía tenemos que pasar curenv como un global:

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

Sospecho que ddply está evaluando en el .GlobalEnv ya, por lo que todos las estrategias parent.frame() y sys.frame() que intenté fracasaron.

 3
Author: Ari B. Friedman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-05 13:12:18