2014-09-08 25 views
5

Sto provando a copiare un oggetto ggplot e poi a modificare alcune proprietà del nuovo oggetto copiato come, ad esempio, la linea di colore in rosso.R ggplot2 comportamento strano. Sembra che stia passando per riferimento

Assumere questo codice:

df = data.frame(cbind(x=1:10, y=1:10)) 
a = ggplot(df, aes(x=x, y=y)) + geom_line() 
b = a 

Poi, se cambio il colore della linea di variabile a

a$layers[[1]]$geom_params$colour = "red" 

cambia anche il colore della b

> b$layers[[1]]$geom_params$colour 
[1] "red" # why it is not "black"? 

Auguro Potrei avere due oggetti diversi a e b con caratteristiche diverse. Quindi, per fare ciò nel modo corretto, avrei bisogno di chiamare di nuovo la trama per b usando b = ggplot(df, aes(xy, y=z)) + geom_line(). Tuttavia, in questo momento nell'algoritmo, non c'è modo di conoscere il comando di trama ggplot(df, aes(x=x, y=y)) + geom_line()

Sai cosa c'è di sbagliato in questo? Gli oggetti ggplot sono trattati in modo diverso?

Grazie!

+0

Bene, sono d'accordo che è un po 'spettrale, ma se guardi a 'str (a)' noterai che i livelli sono oggetti proto, quindi molto probabilmente spiega _why_ stai vedendo questo comportamento. – joran

risposta

7

Il problema qui è che ggplot utilizza la libreria proto per simulare oggetti in stile OO. La libreria proto si basa su ambienti per raccogliere variabili per oggetti. Gli ambienti vengono passati per riferimento, motivo per cui si sta osservando il comportamento che si è (e anche una ragione per cui nessuno probabilmente raccomanderebbe di cambiare le proprietà di un livello in quel modo).

Ad ogni modo, adattando un esempio dal documentario proto, possiamo provare a fare una copia profonda delle later dell'oggetto ggplot. Questo dovrebbe "disconnetterli". Ecco una tale funzione di supporto

duplicate.ggplot<-function(x) { 
    require(proto) 
    r<-x 
    r$layers <- lapply(r$layers, function(x) { 
     as.proto(as.list(x), parent=x) 
    }) 
    r 
} 

quindi se corriamo

df = data.frame(cbind(x=1:10, y=1:10)) 
a = ggplot(df, aes(x=x, y=y)) + geom_line() 
b = a 
c = duplicate.ggplot(a) 

a$layers[[1]]$geom_params$colour = "red" 

quindi tracciano tutti e tre, otteniamo

enter image description here

che dimostra che possiamo cambiare "c" in modo indipendente da " a "

+0

Bello. Stavo riflettendo se c'è un modo canonico di fare un compito (a livello R, non a livello C) che garantisce che una copia sia fatta. – joran

+0

@joran Non conosco alcun modo per forzare una copia profonda di un ambiente nel codice R; ma questo non significa che non esista, credo. – MrFlick

+0

Spiegazione molto bella. Grazie mille! –

3

Ignorando le specifiche di ggplot, c'è un semplice Le trucco per fare una copia completa di (quasi) qualsiasi oggetto in R:

obj_copy <- unserialize(serialize(obj, NULL)) 

Questa serializza l'oggetto in una rappresentazione binaria adatto per la scrittura su disco e poi ricostruisce l'oggetto da quella rappresentazione. È equivalente a salvare l'oggetto in un file e quindi a caricarlo di nuovo (ad esempio saveRDS seguito da readRDS), ma non viene mai salvato in un file. Probabilmente non è la soluzione più efficiente, ma dovrebbe funzionare praticamente per qualsiasi oggetto che può essere salvato su un file.

È possibile definire una funzione deepcopy usando questo trucco:

deepcopy <- function(p) { 
    unserialize(serialize(p, NULL)) 
} 

Questo sembra rompere con successo i legami tra ggplots correlati.

Ovviamente, questo non funzionerà per oggetti che non possono essere serializzati, come le grandi matrici dal pacchetto bigmemory.

+0

Perfetto ........ –