Object not found błąd z ddply wewnątrz funkcji

To naprawdę podważyło moją zdolność do debugowania kodu R.

Chcę użyć ddply(), Aby zastosować te same funkcje do różnych kolumn, które są kolejno nazwane; np. a, b, c. w tym celu zamierzam wielokrotnie przekazywać nazwę kolumny jako ciąg znaków i używać eval(parse(text=ColName)), aby umożliwić funkcji odwołanie się do niej. Chwyciłem tę technikę z innej odpowiedzi.

I to działa dobrze, dopóki nie umieszczę ddply() wewnątrz innej funkcji. Oto przykładowy kod:

# 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
Jakieś pomysły? NewColName jest nawet zdefiniowana wewnątrz funkcji!

Pomyślałem, że odpowiedź na to pytanie, loops-to-create-new-variables-in-ddply , może mi pomóc, ale zrobiłem wystarczająco dużo w głowie na dziś i nadszedł czas, aby podnieść rękę i poprosić o pomoc.

Author: Community, 2011-08-05

5 answers

Możesz to zrobić za pomocą kombinacji do.call i call, aby skonstruować wywołanie w środowisku, w którym NewColName jest nadal widoczne:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-30 08:54:00

Dzisiejszym rozwiązaniem tego pytania jest przekształcenie summarize w here(summarize). np.

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), dodane 2012-02-12 (19: 00)

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-27 18:38:26

Czasami napotykam takie problemy, gdy łączę ddply z summarize lub transform lub czymś takim i nie będąc wystarczająco inteligentnym, aby odkrywać tajniki nawigacji w różnych środowiskach, zwykle robię krok na bok, po prostu nie używając summarize, a zamiast tego używając mojej własnej anonimowej funkcji:

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)

Oczywiście robienie tego "ręcznie" jest kosztowne, ale często unika się bólu głowy związanego z rozwiązywaniem problemów związanych z oceną, które wynikają z połączenia ddply i summarize. To nie do powiedzmy, że Hadley nie znajdzie rozwiązania...

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-05 13:47:21

Problem tkwi w kodzie samego pakietu plyr. W funkcji summarize znajduje się linia eval(substitute(...),.data,parent.frame()). Wiadomo, że rodzic.frame() potrafi robić całkiem fajne i nieoczekiwane rzeczy. T

Rozwiązanie @James jest bardzo dobrym obejściem, ale jeśli dobrze pamiętam, sam @Hadley powiedział wcześniej, że pakiet plyr nie był przeznaczony do użycia w funkcjach.

Przepraszam, myliłem się. Wiadomo jednak, że na razie pakiet plyr daje problemy w takie sytuacje.

Dlatego daję ci podstawowe rozwiązanie problemu:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-07 21:31:44

Wygląda na to, że masz problem ze środowiskiem. Globalny przydział rozwiązuje problem, ale kosztem własnej duszy: {]}

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 szuka rodzica.ramka (1) Więc jeśli zamiast tego zdefiniujesz NewColName poza Mojafunkcją, powinno działać:

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)

Używając get do wyciągnięcia mojego.parse z wcześniejszego środowiska, możemy zbliżyć się znacznie bliżej, ale nadal musimy przekazać curenv jako globalny:

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

Podejrzewam, że ddply ocenia wGlobalEnv już, dlatego wszystkie strategie parent.frame() i sys.frame(), które wypróbowałem, zawiodły.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-05 13:12:18