¿Trazar una leyenda fuera del área de trazado en gráficos base?

185

Como dice el título: ¿Cómo puedo trazar una leyenda fuera del área de trazado cuando uso gráficos básicos?

Pensé en jugar layouty producir una trama vacía para contener solo la leyenda, pero me interesaría de alguna manera usar solo las instalaciones del gráfico base y, por ejemplo, par(mar = )obtener algo de espacio a la derecha de la trama para la leyenda.


Aquí un ejemplo:

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:

texto alternativo

Pero como se dijo, me gustaría que la leyenda esté fuera del área de trazado (por ejemplo, a la derecha del gráfico / diagrama).

Henrik
fuente
... también puede hackear par con contenedor ficticio para la leyenda, de vez en cuando fácil y bastante conveniente. Pregunta similar aquí .
hhh
3
@hhh El enlace ya no funciona. ¿Puedes actualizarlo o publicar una respuesta usando este enfoque?
Henrik

Respuestas:

111

Quizás lo que necesita es par(xpd=TRUE)permitir que las cosas se dibujen fuera de la región de la trama. Entonces, si haces la trama principal bty='L', tendrás un espacio a la derecha para una leyenda. Normalmente, esto se recortaría en la región de la trama, pero hazlo par(xpd=TRUE)y con un poco de ajuste puedes obtener una leyenda lo más a la derecha posible:

 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))
Hombre espacial
fuente
33
Tenga en cuenta que puede pasar xpd directamente a la leyenda para que no tenga que preocuparse por restablecer el par después. También vea grconvertX & Y para una forma de especificar la ubicación de la leyenda de una manera que no dependa de los límites de los datos que está trazando.
Charles
66
Como esta pregunta y respuesta siguen siendo muy populares, par(xpd=NA)es aún más poderosa (es decir, parcelas en más regiones).
Henrik
+1. Debemos mencionar que tiene sentido tener una parllamada separada justo antes de la leyenda. En mi argumento, utilicé par(new=T)en varias otras ocasiones y simplemente quería agregar el xpdparámetro en la misma llamada, lo que causa problemas.
Matt Bannert
146

Nadie ha mencionado el uso de insetvalores negativos para legend. Aquí hay un ejemplo, donde la leyenda está a la derecha del diagrama, alineada a la parte superior (usando la palabra clave "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")

El primer valor de inset=c(-0.2,0)podría necesitar un ajuste basado en el ancho de la leyenda.

legend_right

Mike T
fuente
14
@Henrik no, no funciona sin xpd = TRUE. También tenga en cuenta que es mejor establecer xpd = TRUE como argumento de la función legend ().
Stéphane Laurent
1
A veces xpddebe establecerse en TRUEpara que funcione el recuadro negativo. Pero a veces no. Con el comando args.legend=list(x="bottom", horiz=TRUE, inset=-0.2)dentro de un barplot(...no parece necesitar, xpd=TRUEpero con solo legend(x="bottom", horiz=TRUE, inset=-0.2)parece necesitar xpd=TRUE. Alguna idea? ¿Estoy confundido al pasar mis argumentos?
user3386170
28

Otra solución, además de las ondes ya mencionadas (usando layouto par(xpd=TRUE)) es superponer su trama con una trama transparente sobre todo el dispositivo y luego agregarle la leyenda.

El truco consiste en superponer un gráfico (vacío) sobre el área de trazado completa y agregarle la leyenda. Podemos usar la par(fig=...)opción. Primero le indicamos a R que cree una nueva trama sobre todo el dispositivo de trazado:

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

Configuración omay mares necesaria ya que queremos que el interior de la trama cubra todo el dispositivo. new=TRUEes necesario para evitar que R inicie un nuevo dispositivo. Luego podemos agregar el diagrama vacío:

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

Y estamos listos para agregar la leyenda:

legend("bottomright", ...)

agregará una leyenda en la parte inferior derecha del dispositivo. Del mismo modo, podemos agregar la leyenda al margen superior o derecho. Lo único que debemos asegurar es que el margen de la trama original sea lo suficientemente grande como para acomodar la leyenda.

Poniendo todo esto en una función;

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(...)
}

Y un ejemplo. Primero cree la trama asegurándose de que tengamos suficiente espacio en la parte inferior para agregar la leyenda:

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

Luego agrega la leyenda

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

Resultando en:

La figura de ejemplo muestra la leyenda en el margen superior

Jan van der Laan
fuente
2
Gran adición a la lista aquí. Hay una explicación acerca de cómo hacer este trabajo con múltiples tramas en el gráfico aquí .
shiri
Jan, ¿hay alguna forma de aumentar el tamaño de fuente en la leyenda, sin recortar texto? Por ejemplo, tengo un gráfico de 4 tipos diferentes de etiquetas, pero con mucho espacio vacío entre ellas.
Un viejo en el mar.
He escrito una pregunta con más detalles stackoverflow.com/questions/42707308/…
Un anciano en el mar.
16

Perdón por resucitar un hilo viejo, pero hoy tuve el mismo problema. La forma más simple que he encontrado es la siguiente:

# 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)

Encontrado aquí: http://www.harding.edu/fmccown/R/

veiga
fuente
44
Aún mejor es oldpar <- par (xpd = T, mar = par () $ mar + c (0,0,0,6)) ... par (oldpar) (Ver la ayuda de par)
rakensi
Esta solución es mejor porque el espacio para la leyenda es fijo, no importa la longitud de las cadenas de la leyenda
Sergio
15

Me gusta hacerlo así:

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))

ingrese la descripción de la imagen aquí

El único ajuste requerido es establecer el margen correcto para que sea lo suficientemente ancho como para acomodar la leyenda.

Sin embargo, esto también se puede automatizar:

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))

ingrese la descripción de la imagen aquí

jbaums
fuente
El uso de xpd = T o xpd = NA no impide que mi 'main' (título) se recorte cuando se estira para intentar usar el área agregada con el amplio margen derecho.
Phil Goetz
@ PhilGoetz, ¿estás seguro de que estás trazando el principal dentro del área de trazado? ¿Es posible que no tenga suficientes líneas de margen allí para trazar?
jbaums
10

Recientemente encontré una función muy fácil e interesante para imprimir leyendas fuera del área de la trama donde quieras.

Haga el margen exterior en el lado derecho de la 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")

Agregue la leyenda y simplemente use la función de localización (1) como se muestra a continuación. Luego debe hacer clic donde desee después de cargar el siguiente script.

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

Intentalo

Vandka
fuente
9

Solo puedo ofrecer un ejemplo de la solución de diseño ya señalada.

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))

una imagen fea: S

Roman Luštrik
fuente
9

Agregar otra alternativa simple que es bastante elegante en mi opinión.

Su 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")

Leyenda:

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"
       )

Resultado:

cuadro con leyenda

Aquí solo se agregó la segunda línea de la leyenda a su ejemplo. En turno:

  • inset=c(0,1)- mueve la leyenda por fracción de la región de la trama en direcciones (x, y). En este caso la leyenda está en "bottomright"posición. Se mueve por 0 regiones de trazado en la dirección x (por lo que permanece en "derecha") y por 1 región de trazado en la dirección y (de abajo hacia arriba). Y sucede que aparece justo encima de la trama.
  • xpd=TRUE - dejemos que la leyenda aparezca fuera de la región de trazado.
  • horiz=TRUE - da instrucciones para producir una leyenda horizontal.
  • bty="n" - Un detalle de estilo para deshacerse del cuadro delimitador de leyendas.

Lo mismo se aplica al agregar la leyenda al lado:

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"
       )

Aquí simplemente ajustamos las posiciones de la leyenda y agregamos espacio de margen adicional al lado derecho de la gráfica. Resultado:

imagen con leyenda 2

Karolis Koncevičius
fuente
4

Puede hacer esto con la API Plotly R , ya sea con código o desde la GUI arrastrando la leyenda a donde lo desee.

Aquí hay un ejemplo. El gráfico y el código también están aquí .

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)

Puede colocar la leyenda fuera del gráfico asignando uno de los valores x e y a 100 o -100.

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

Aquí están las otras opciones:

  • list("x" = 100, "y" = 0) para exterior derecho inferior
  • list("x" = 100, "y"= 1) Exterior derecho superior
  • list("x" = 100, "y" = .5) Exterior derecho medio
  • list("x" = 0, "y" = -100) Bajo izquierda
  • list("x" = 0.5, "y" = -100) Bajo centro
  • list("x" = 1, "y" = -100) Bajo derecho

Entonces la respuesta.

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

Plotly devuelve una URL con su gráfico cuando realiza una llamada. Puede acceder a eso más rápidamente llamando browseURL(response$url)para que abra su gráfico en su navegador.

url = response$url
filename = response$filename

Eso nos da este gráfico. También puede mover la leyenda desde la GUI y luego el gráfico se escalará en consecuencia. Divulgación completa: estoy en el equipo de Plotly.

Leyenda en el lado del gráfico

Mateo Sanchez
fuente
2

Pruebe layout()lo que he usado para esto en el pasado simplemente creando una gráfica vacía a continuación, ajustada adecuadamente a aproximadamente 1/4 y colocando las partes de la leyenda manualmente en ella.

Aquí hay algunas preguntas anteriores sobre legend()cuáles deberían ayudarlo a comenzar.

Dirk Eddelbuettel
fuente
Como ya dije en la pregunta, esto es lo que pensé también. Pero sería ideal, si hubiera otra forma. De alguna manera, supongo que no.
Henrik