Tracciare una legenda al di fuori dell'area di disegno nella grafica di base?


185

Come dice il titolo: Come posso tracciare una legenda al di fuori dell'area di stampa quando utilizzo la grafica di base?

Ho pensato di armeggiare layoute produrre un grafico vuoto per contenere solo la legenda, ma sarei interessato in un modo usando solo le strutture del grafico di base e, par(mar = )ad esempio, per ottenere un po 'di spazio sulla destra del diagramma per la legenda.


Ecco un esempio:

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
legend(1,-1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

produce:

testo alternativo

Ma come detto, vorrei che la legenda si trovasse al di fuori dell'area di disegno (ad esempio, a destra del grafico / trama.


... puoi anche hackerare il par con un contenitore fittizio per la leggenda, facile e abbastanza conveniente di volta in volta. Domanda simile qui .
eh

3
@hhh Il link non funziona più. Puoi aggiornarlo o pubblicare una risposta usando questo approccio?
Henrik,

Risposte:


111

Forse ciò di cui hai bisogno è par(xpd=TRUE)consentire che le cose vengano disegnate al di fuori dell'area della trama. Quindi, se fai la trama principale bty='L', avrai un po 'di spazio sulla destra per una leggenda. Normalmente questo verrebbe ritagliato nella regione della trama, ma farlo par(xpd=TRUE)e con un po 'di regolazione puoi ottenere una legenda fino in fondo:

 set.seed(1) # just to get the same random numbers
 par(xpd=FALSE) # this is usually the default

 plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2), bty='L')
 # this legend gets clipped:
 legend(2.8,0,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

 # so turn off clipping:
 par(xpd=TRUE)
 legend(2.8,-1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

33
Nota che puoi passare xpd direttamente alla legenda, quindi non devi preoccuparti di ripristinare il par in seguito. Vedi anche grconvertX & Y per un modo per specificare la posizione della legenda in modo non dipendente dai limiti dei dati che stai pianificando.
Charles,

6
poiché questa domanda e risposta sono ancora molto popolari, par(xpd=NA)è ancora più potente (vale a dire, trame in più regioni).
Henrik,

+1. Dobbiamo ricordare che ha senso avere una parchiamata separata proprio prima della legenda. Nella mia trama, ho usato par(new=T)in diverse altre occasioni e volevo semplicemente aggiungere il xpdparametro nella stessa chiamata, il che causa problemi.
Matt Bannert,

146

Nessuno ha menzionato l'utilizzo di insetvalori negativi per legend. Ecco un esempio, in cui la legenda si trova a destra della trama, allineata in alto (usando la parola chiave "topright").

# Random data to plot:
A <- data.frame(x=rnorm(100, 20, 2), y=rnorm(100, 20, 2))
B <- data.frame(x=rnorm(100, 21, 1), y=rnorm(100, 21, 1))

# Add extra space to right of plot area; change clipping to figure
par(mar=c(5.1, 4.1, 4.1, 8.1), xpd=TRUE)

# Plot both groups
plot(y ~ x, A, ylim=range(c(A$y, B$y)), xlim=range(c(A$x, B$x)), pch=1,
               main="Scatter plot of two groups")
points(y ~ x, B, pch=3)

# Add legend to top right, outside plot region
legend("topright", inset=c(-0.2,0), legend=c("A","B"), pch=c(1,3), title="Group")

inset=c(-0.2,0)Potrebbe essere necessario regolare il primo valore di in base alla larghezza della legenda.

legend_right


14
@Henrik no, non funziona senza xpd = TRUE. Si noti inoltre che è meglio impostare xpd = TRUE come argomento della funzione legenda ().
Stéphane Laurent,

1
A volte xpddeve essere impostato su TRUEaffinché l'inserzione negativa funzioni. Ma a volte no. Con il comando args.legend=list(x="bottom", horiz=TRUE, inset=-0.2)all'interno di un barplot(...sembra non aver bisogno, xpd=TRUEma con solo legend(x="bottom", horiz=TRUE, inset=-0.2)sembra aver bisogno xpd=TRUE. Qualche intuizione? Sono solo confuso nel passare le mie discussioni?
user3386170

28

Un'altra soluzione, oltre agli ondes già menzionati (usando layouto par(xpd=TRUE)) è quella di sovrapporre la trama con una trama trasparente sull'intero dispositivo e quindi aggiungere la legenda a quella.

Il trucco è quello di sovrapporre un grafico (vuoto) sull'intera area di stampa e aggiungere la legenda a quella. Possiamo usare l' par(fig=...)opzione. Innanzitutto, chiediamo a R di creare un nuovo diagramma sull'intero dispositivo di stampa:

par(fig=c(0, 1, 0, 1), oma=c(0, 0, 0, 0), mar=c(0, 0, 0, 0), new=TRUE)

L'impostazione omaed marè necessaria poiché vogliamo che l'interno della trama copra l'intero dispositivo. new=TRUEè necessario per impedire a R di avviare un nuovo dispositivo. Possiamo quindi aggiungere la trama vuota:

plot(0, 0, type='n', bty='n', xaxt='n', yaxt='n')

E siamo pronti per aggiungere la legenda:

legend("bottomright", ...)

aggiungerà una legenda in basso a destra del dispositivo. Allo stesso modo, possiamo aggiungere la legenda al margine superiore o destro. L'unica cosa che dobbiamo garantire è che il margine della trama originale sia abbastanza grande da accogliere la leggenda.

Mettere tutto questo in una funzione;

add_legend <- function(...) {
  opar <- par(fig=c(0, 1, 0, 1), oma=c(0, 0, 0, 0), 
    mar=c(0, 0, 0, 0), new=TRUE)
  on.exit(par(opar))
  plot(0, 0, type='n', bty='n', xaxt='n', yaxt='n')
  legend(...)
}

E un esempio Per prima cosa crea la trama assicurandoti di avere abbastanza spazio in basso per aggiungere la legenda:

par(mar = c(5, 4, 1.4, 0.2))
plot(rnorm(50), rnorm(50), col=c("steelblue", "indianred"), pch=20)

Quindi aggiungi la legenda

add_legend("topright", legend=c("Foo", "Bar"), pch=20, 
   col=c("steelblue", "indianred"),
   horiz=TRUE, bty='n', cex=0.8)

Con il risultato di:

Figura di esempio mostrata legenda nel margine superiore


2
Grande aggiunta alla lista qui. C'è una spiegazione su come farlo funzionare con più grafici nel grafico qui .
Shiri,

Jan, c'è un modo per aumentare la dimensione del carattere nella legenda, senza che del testo venga troncato? Ad esempio, ho un grafico di 4 diversi tipi di etichette, ma con molto spazio vuoto tra di loro.
Un vecchio nel mare.

Ho scritto una domanda con maggiori dettagli stackoverflow.com/questions/42707308/…
Un vecchio nel mare.

16

Ci scusiamo per aver resuscitato un vecchio thread, ma oggi ho avuto lo stesso problema. Il modo più semplice che ho trovato è il seguente:

# Expand right side of clipping rect to make room for the legend
par(xpd=T, mar=par()$mar+c(0,0,0,6))

# Plot graph normally
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

# Plot legend where you want
legend(3.2,1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

# Restore default clipping rect
par(mar=c(5, 4, 4, 2) + 0.1)

Trovato qui: http://www.harding.edu/fmccown/R/


4
Ancora meglio è oldpar <- par (xpd = T, mar = par () $ mar + c (0,0,0,6)) ... par (oldpar) (Vedi l'aiuto di par)
rakensi

Questa soluzione è migliore perché lo spazio per la legenda è fisso indipendentemente dalla lunghezza delle stringhe della legenda
Sergio,

15

Mi piace farlo così:

par(oma=c(0, 0, 0, 5))
plot(1:3, rnorm(3), pch=1, lty=1, type="o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch=2, lty=2, type="o")
legend(par('usr')[2], par('usr')[4], bty='n', xpd=NA,
       c("group A", "group B"), pch=c(1, 2), lty=c(1,2))

inserisci qui la descrizione dell'immagine

L'unica modifica richiesta è quella di impostare il margine giusto in modo che sia sufficientemente ampio da contenere la legenda.

Tuttavia, questo può anche essere automatizzato:

dev.off() # to reset the graphics pars to defaults
par(mar=c(par('mar')[1:3], 0)) # optional, removes extraneous right inner margin space
plot.new()
l <- legend(0, 0, bty='n', c("group A", "group B"), 
            plot=FALSE, pch=c(1, 2), lty=c(1, 2))
# calculate right margin width in ndc
w <- grconvertX(l$rect$w, to='ndc') - grconvertX(0, to='ndc')
par(omd=c(0, 1-w, 0, 1))
plot(1:3, rnorm(3), pch=1, lty=1, type="o", ylim=c(-2, 2))
lines(1:3, rnorm(3), pch=2, lty=2, type="o")
legend(par('usr')[2], par('usr')[4], bty='n', xpd=NA,
       c("group A", "group B"), pch=c(1, 2), lty=c(1, 2))

inserisci qui la descrizione dell'immagine


L'uso di xpd = T o xpd = NA non impedisce che il mio "principale" (titolo) venga troncato quando viene allungato per provare a utilizzare l'area aggiunta con il margine destro ampio.
Phil Goetz,

@PhilGoetz sei sicuro di aver tracciato il main nell'area della trama? È possibile che tu non abbia abbastanza linee di margine lì per tracciare?
jbaums,

10

Recentemente ho trovato una funzione molto semplice e interessante per stampare legende al di fuori dell'area della trama in cui si desidera.

Crea il margine esterno sul lato destro della trama.

par(xpd=T, mar=par()$mar+c(0,0,0,5))

Crea una trama

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

Aggiungi legenda e usa semplicemente la funzione di localizzazione (1) come di seguito. Quindi devi solo fare clic dove vuoi dopo aver caricato il seguente script.

legend(locator(1),c("group A", "group B"), pch = c(1,2), lty = c(1,2))

Provalo


9

Posso offrire solo un esempio della soluzione di layout già indicata.

layout(matrix(c(1,2), nrow = 1), widths = c(0.7, 0.3))
par(mar = c(5, 4, 4, 2) + 0.1)
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
par(mar = c(5, 0, 4, 2) + 0.1)
plot(1:3, rnorm(3), pch = 1, lty = 1, ylim=c(-2,2), type = "n", axes = FALSE, ann = FALSE)
legend(1, 1, c("group A", "group B"), pch = c(1,2), lty = c(1,2))

un'immagine brutta: S


9

Aggiungendo un'altra semplice alternativa che è abbastanza elegante secondo me.

La tua trama:

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

Leggenda:

legend("bottomright", c("group A", "group B"), pch=c(1,2), lty=c(1,2),
       inset=c(0,1), xpd=TRUE, horiz=TRUE, bty="n"
       )

Risultato:

foto con leggenda

Qui solo la seconda riga della legenda è stata aggiunta al tuo esempio. A sua volta:

  • inset=c(0,1)- sposta la legenda per frazione della regione del diagramma nelle direzioni (x, y). In questo caso la legenda è in "bottomright"posizione. Viene spostato da 0 aree del tracciato in direzione x (quindi rimane su "destra") e da 1 regione del tracciato in direzione y (dal basso verso l'alto). E succede così che appare proprio sopra la trama.
  • xpd=TRUE - Facciamo apparire la legenda al di fuori dell'area della trama.
  • horiz=TRUE - indica di produrre una legenda orizzontale.
  • bty="n" - un dettaglio di stile per sbarazzarsi del riquadro di selezione della legenda.

Lo stesso vale quando si aggiunge la legenda al lato:

par(mar=c(5,4,2,6))
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

legend("topleft", c("group A", "group B"), pch=c(1,2), lty=c(1,2),
       inset=c(1,0), xpd=TRUE, bty="n"
       )

Qui abbiamo semplicemente regolato le posizioni delle legende e aggiunto ulteriore spazio sul margine sul lato destro della trama. Risultato:

foto con legenda 2


4

Puoi farlo con l' API Plotly R , con entrambi i codici o dalla GUI trascinando la legenda dove vuoi.

Ecco un esempio Il grafico e il codice sono anche qui .

x = c(0,1,2,3,4,5,6,7,8) 
y = c(0,3,6,4,5,2,3,5,4) 
x2 = c(0,1,2,3,4,5,6,7,8) 
y2 = c(0,4,7,8,3,6,3,3,4)

È possibile posizionare la legenda all'esterno del grafico assegnando uno dei valori xey a 100 o -100.

legendstyle = list("x"=100, "y"=1)
layoutstyle = list(legend=legendstyle)

Ecco le altre opzioni:

  • list("x" = 100, "y" = 0) per il fondo esterno destro
  • list("x" = 100, "y"= 1) In alto a destra in alto
  • list("x" = 100, "y" = .5) Medio destro esterno
  • list("x" = 0, "y" = -100) Sotto a sinistra
  • list("x" = 0.5, "y" = -100) Sotto il centro
  • list("x" = 1, "y" = -100) Sotto a destra

Quindi la risposta.

response = p$plotly(x,y,x2,y2, kwargs=list(layout=layoutstyle));

Restituisce in modo completo un URL con il tuo grafico quando effettui una chiamata. Puoi accedervi più rapidamente chiamando in browseURL(response$url)modo da aprire il tuo grafico nel tuo browser.

url = response$url
filename = response$filename

Questo ci dà questo grafico. È inoltre possibile spostare la legenda all'interno della GUI e il grafico verrà ridimensionato di conseguenza. Divulgazione completa: faccio parte del team di Plotly.

Legenda sul lato del grafico


2

Prova quello layout()che ho usato per questo in passato semplicemente creando un diagramma vuoto di seguito, ridimensionato correttamente a circa 1/4 o giù di lì e posizionando manualmente le parti della legenda.

Ci sono alcune domande più vecchie qui su legend()quale dovrebbe iniziare.


Come già detto nella domanda, anche questo è ciò a cui ho pensato. Ma sarebbe l'ideale, se ci fosse un altro modo. In qualche modo suppongo che non lo sia.
Henrik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.