2012-05-18 25 views
45

Ich versuche, eine lokale Variable in aes zu verwenden, wenn ich mit ggplot plotten. Das ist mein Problem zum Wesen eingekocht:Lokale Variablen Innerhalb aes

xy <- data.frame(x=1:10,y=1:10) 

plotfunc <- function(Data,YMul=2){ 
    ggplot(Data,aes(x=x,y=y*YMul))+geom_line() 
} 

plotfunc(xy) 

Diese in den folgenden Fehlern führt:

Error in eval(expr, envir, enclos) : object 'YMul' not found 

Es scheint, als ob ich nicht auf lokale Variablen (oder Funktionsargumente) in aes verwenden kann. Könnte es sein, dass es aufgrund des Inhalts von aes später ausgeführt wird, wenn die lokale Variable außerhalb des Geltungsbereichs ist? Wie kann ich dieses Problem vermeiden (abgesehen davon, dass die lokale Variable aes nicht verwendet wird)?

+0

Ich denke, weil es immer noch erwartet, dass die Ymul zu überliefern, aber sie geben nur plotfunc (xy) – zhan2383

+0

nicht wahr ist, sollte es verwenden, die Standardwert – baptiste

+0

Ich führe den Code oben und bekomme keinen Fehler (23 Oktober 2017), gab es ein Update auf '' ggplot2'' um zu erklären, warum das jetzt funktionieren würde? – PatrickT

Antwort

37

I würde die lokale Umgebung erfassen,

xy <- data.frame(x=1:10,y=1:10) 

plotfunc <- function(Data, YMul = 2){ 
    .e <- environment() 
    ggplot(Data, aes(x = x, y = y*YMul), environment = .e) + geom_line() 
} 

plotfunc(xy) 
+0

Vielleicht ist das der offizielle (aber undokumentierte) Weg, denke ich. – kohske

+8

um ehrlich zu sein, ich denke es sollte irgendwie der Standard sein. Das gleiche gilt für Plyr, ich bin immer sehr verwirrt, wenn ** ply keine Variable findet, die andere R-Funktionen mit den üblichen Scoping-Regeln finden würden. – baptiste

+2

+1 - @kohske und @ baptiste. Ich mag das auch am besten.Bemerkenswert ist jedoch, dass es etwas anderes als meine Lösung tut, wie man sehen kann durch: (1) Entfernen von "y = 1: 10" von dem Datenrahmen "xy"; (2) Setzen von "y <-1: 10" in die globale Umgebung; und (3) Setzen von "y <-10: 1" in den Funktionskörper vor dem Aufruf von ggplot. Im Wesentlichen ermöglicht meine Lösung das Übergeben ausgewählter Argumente, ohne die Bereichsregeln anderweitig zu ändern. Ihr ändert vollständig das Scoping-Verhalten von 'ggplot()' (weshalb ich es mag). –

5

ggplot() ‚s aes erwartet YMul eine Variable innerhalb des data Datenrahmen zu sein. Versuchen einschließlich YMull dort statt:

Dank @Justin: ggplot() ‚s aes für YMul im data Datenrahmen zuerst zu schauen scheint, und wenn nicht gefunden, dann in der globalen Umwelt. Ich möchte solche Variablen dem Datenrahmen wie folgt hinzufügen, da es für mich konzeptionell Sinn macht. Ich muss mir auch keine Gedanken über Änderungen an globalen Variablen machen, die unerwartete Konsequenzen für Funktionen haben. Aber alle anderen Antworten sind auch richtig. Also, verwenden Sie, was Ihnen passt.

require("ggplot2") 
xy <- data.frame(x = 1:10, y = 1:10) 
xy <- cbind(xy, YMul = 2) 

ggplot(xy, aes(x = x, y = y * YMul)) + geom_line() 

Oder, wenn Sie die Funktion in Ihrem Beispiel wollen:

plotfunc <- function(Data, YMul = 2) 
{ 
    ggplot(cbind(Data, YMul), aes(x = x, y = y * YMul)) + geom_line() 
} 

plotfunc(xy) 
+2

'YMul' muss nicht Teil des data.frames sein. Es muss nur in dem Bereich definiert werden, in dem das Objekt "ggplot" ausgewertet wird, das global ist und nicht in der Funktion. – Justin

+0

@ Justin: Danke. Das hatte ich nicht bemerkt. Interessant, dass 'ggplot()' im Datenrahmen zuerst nach 'YMul' zu suchen scheint und dann, wenn nicht in der globalen Umgebung gefunden, scheinbar die Argumente der Funktion überspringt. Ich habe keine Informationen darüber gefunden, wie 'ggplot()' die Namespaces durchsucht, aber andererseits habe ich mich nicht sehr angestrengt. – jthetzel

+0

Dies ist bei weitem die leichtere Option zum Merken und Tippen. Vielleicht nicht großartig, wenn Ihr Datenrahmen eine Milliarde Zeilen hat, aber für andere Situationen geeignet ist. – PatrickT

0

Wenn Sie Ihren Code außerhalb der Funktion ausführen es funktioniert. Und wenn Sie den Code innerhalb der Funktion mit YMul global definiert ausführen, funktioniert es. Ich verstehe nicht ganz, das Innenleben von ggplot aber das funktioniert ...

YMul <- 2 

plotfunc <- function(Data){ 
    ggplot(Data,aes(x=x,y=y*YMul))+geom_line() 
} 

plotfunc(xy) 
10

Hier ist eine Alternative, die Sie in einem beliebigen Wert durch das YMul Argument übergeben können, ohne es an die Data data.frame hinzufügen zu müssen oder für die globale Umwelt:

plotfunc <- function(Data, YMul = 2){ 
    eval(substitute(
     expr = { 
      ggplot(Data,aes(x=x,y=y*YMul)) + geom_line() 
     }, 
     env = list(YMul=YMul))) 
    } 

plotfunc(xy, YMul=100) 

um zu sehen, wie das funktioniert, versuchen Sie die folgende Zeile in Isolation aus:

substitute({ggplot(Data, aes(x=x, y=y*YMul))}, list(YMul=100)) 
+0

+1 Danke! Ich wusste, dass es einen Weg gab, es zu tun, aber ich hatte mir nie die Zeit genommen, das zu klären. Sehr cool. – Justin

+1

@Justin - Froh, dass das hilfreich ist. Was * ich * nie die Zeit genommen habe, um herauszufinden, wie ggplot's Proto-basiertes Scoping funktioniert. Als Beispiel habe ich einfach eine 'browser()' Anweisung in den 'aes()' Aufruf eingefügt, und nach der Eingabe von 'sys.frames()' erhalte ich eine Liste von 23 Umgebungen, von denen keine (??) scheinen einen direkten Zugriff auf den Wert von YMul zu ermöglichen. Hmm. –

+0

Ja ... es geht über mich hinaus. Ich hatte etwas [custom code] (http://stackoverflow.com/questions/9586882/new-ggplot2-and-custom-boxplot-code) mit freundlicher Genehmigung von Kohske, das mit der neuen Version brach und für mich völlig undurchschaubar ist! Irgendwann würde ich gerne Proto ... – Justin

1

Haben Sie die Lösung von @wch (W. Chang)?

https://github.com/hadley/ggplot2/issues/743

Ich denke, es ist die bessere

ist im Wesentlichen wie der @baptiste sondern umfassen den Verweis auf die Umgebung direkt im Aufruf

ich es berichten hier

ggplot
g <- function() { 
    foo3 <- 4 
    ggplot(mtcars, aes(x = wt + foo3, y = mpg), 
     environment = environment()) + 
    geom_point() 
} 

g() 
# Works 
+0

Dies ist ein Duplikat der Antwort von @ baptiste, und IMO verwendet unnötigerweise ein anderes Beispiel, wenn OP ein reproduzierbares Beispiel liefert. Ich würde vorschlagen, zu entfernen (und vielleicht Baptisten mit der Github-Frage zu kommentieren, die Sie verbunden haben). –

4

Ich verwende ggplot2, und Ihr Beispiel scheint mit der aktuellen Version gut zu funktionieren.

Es ist jedoch leicht, Varianten zu finden, die immer noch Probleme bereiten. Ich war selbst von ähnlichem Verhalten verwirrt, und so habe ich diesen Beitrag gefunden (Top-Google-Ergebnis für "ggplot, wie man Variablen bewertet, wenn sie bestanden wurden"). Zum Beispiel bewegen, wenn wir aus plotfunc ggplot:

xy <- data.frame(x=1:10,y=1:10) 

plotfunc <- function(Data,YMul=2){ 
    geom_line(aes(x=x,y=y*YMul)) 
} 

ggplot(xy)+plotfunc(xy) 
# Error in eval(expr, envir, enclos) : object 'YMul' not found 

In der obigen Variante „die lokale Umgebung erfassen“ ist keine Lösung, weil ggplot nicht innerhalb der Funktion, und nur ggplot hat die „Umwelt genannt wird = "Argument.

Aber es gibt jetzt eine Familie von Funktionen "aes_", "aes_string", "aes_q", die wie "aes" sind, aber lokale Variablen erfassen. Wenn wir oben "aes_" verwenden, erhalten wir immer noch einen Fehler, weil es jetzt nichts über "x" weiß. Aber es ist einfach direkt auf die Daten zu beziehen, die das Problem löst:

plotfunc <- function(Data,YMul=2){ 
    geom_line(aes_(x=Data$x,y=Data$y*YMul)) 
} 
ggplot(xy)+plotfunc(xy) 
# works