Tracer une légende en dehors de la zone de traçage dans les graphiques de base?

185

Comme le titre l'indique: Comment puis-je tracer une légende en dehors de la zone de traçage lors de l'utilisation de graphiques de base?

J'ai pensé à bidouiller layoutet à produire un tracé vide pour ne contenir que la légende, mais je serais intéressé d'une manière en utilisant uniquement les fonctionnalités du graphe de base et par exemple, par(mar = )pour obtenir de l'espace à droite du tracé pour la légende.


Voici un exemple:

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

produit:

texte alternatif

Mais comme dit, je voudrais que la légende soit en dehors de la zone de traçage (par exemple, à droite du graphe / tracé.

Henrik
la source
... vous pouvez également pirater avec un conteneur factice pour la légende, facile et assez pratique de temps en temps. Question similaire ici .
hhh
3
@hhh Le lien ne fonctionne plus. Pouvez-vous le mettre à jour ou publier une réponse en utilisant cette approche?
Henrik

Réponses:

111

Peut-être que vous avez besoin par(xpd=TRUE)de permettre aux objets d'être dessinés en dehors de la zone de tracé. Donc, si vous faites l'intrigue principale avec, bty='L'vous aurez de l'espace sur la droite pour une légende. Normalement, cela serait découpé dans la région du tracé, mais faites-le par(xpd=TRUE)et avec un peu d'ajustement, vous pouvez obtenir une légende aussi loin que possible:

 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))
Spacedman
la source
33
Notez que vous pouvez passer directement xpd à la légende afin que vous n'ayez pas à vous soucier de la réinitialisation par la suite. Voir également grconvertX & Y pour un moyen de spécifier l'emplacement de la légende d'une manière indépendante des limites des données que vous tracez.
Charles
6
comme cette question et cette réponse sont toujours très populaires, elle par(xpd=NA)est encore plus puissante (c.-à-d. parcelles vers plus de régions).
Henrik
+1. Nous devons mentionner qu'il est logique d'avoir un parappel séparé juste avant la légende. Dans mon intrigue, j'ai utilisé par(new=T)à plusieurs autres occasions et je voulais simplement ajouter le xpdparamètre dans le même appel, ce qui cause des problèmes.
Matt Bannert
146

Personne n'a mentionné l'utilisation de insetvaleurs négatives pour legend. Voici un exemple, où la légende est à droite du graphique, alignée vers le haut (à l'aide du mot-clé "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")

La première valeur de inset=c(-0.2,0)peut nécessiter un ajustement en fonction de la largeur de la légende.

legend_right

Mike T
la source
14
@Henrik non ça ne marche pas sans xpd = TRUE. Notez également qu'il est préférable de définir xpd = TRUE comme argument de la fonction legend ().
Stéphane Laurent
1
Parfois, xpddoit être réglé sur TRUEpour que l'encart négatif fonctionne. Mais parfois non. Avec la commande args.legend=list(x="bottom", horiz=TRUE, inset=-0.2)dans un, barplot(...il ne semble pas avoir besoin xpd=TRUEmais avec juste legend(x="bottom", horiz=TRUE, inset=-0.2)cela semble avoir besoin xpd=TRUE. Des idées? Je suis juste confus en passant mes arguments?
user3386170
28

Une autre solution, en plus des ondes déjà mentionnées (en utilisant layoutou par(xpd=TRUE)), consiste à superposer votre tracé avec un tracé transparent sur l'ensemble de l'appareil, puis à y ajouter la légende.

L'astuce consiste à superposer un graphique (vide) sur toute la zone de traçage et à y ajouter la légende. Nous pouvons utiliser l' par(fig=...)option. Nous demandons d'abord à R de créer un nouveau tracé sur tout le périphérique de traçage:

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

Réglage omaet marest nécessaire car nous voulons que l'intérieur de la parcelle recouvre l'ensemble de l'appareil. new=TRUEest nécessaire pour empêcher R de démarrer un nouvel appareil. Nous pouvons ensuite ajouter le tracé vide:

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

Et nous sommes prêts à ajouter la légende:

legend("bottomright", ...)

ajoutera une légende en bas à droite de l'appareil. De même, nous pouvons ajouter la légende dans la marge supérieure ou droite. La seule chose que nous devons nous assurer est que la marge de l'intrigue originale est suffisamment grande pour accueillir la légende.

Mettre tout cela en fonction;

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

Et un exemple. Commencez par créer l'intrigue en vous assurant que nous avons suffisamment d'espace en bas pour ajouter la légende:

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

Puis ajoutez la légende

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

Résultant en:

Exemple de figure avec légende dans la marge supérieure

Jan van der Laan
la source
2
Excellent ajout à la liste ici. Il y a une explication sur la façon de faire fonctionner cela avec plusieurs tracés dans le graphique ici .
shiri
Jan, y a-t-il un moyen d'augmenter la taille de la police dans la légende, sans que du texte ne soit coupé? Par exemple, j'ai un graphique 4 types d'étiquettes différents, mais avec beaucoup d'espace vide entre eux.
Un vieil homme dans la mer.
J'ai écrit une question avec plus de détails stackoverflow.com/questions/42707308/…
Un vieil homme dans la mer.
16

Désolé d'avoir ressuscité un vieux fil, mais j'étais avec le même problème aujourd'hui. Le moyen le plus simple que j'ai trouvé est le suivant:

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

Trouvé ici: http://www.harding.edu/fmccown/R/

Veiga
la source
4
Encore mieux est oldpar <- par (xpd = T, mar = par () $ mar + c (0,0,0,6)) ... par (oldpar) (Voir l'aide de par)
rakensi
Cette solution est meilleure car l'espace pour la légende est fixe quelle que soit la longueur des chaînes de légende
Sergio
15

J'aime le faire comme ça:

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

entrez la description de l'image ici

Le seul ajustement requis est de définir la marge de droite pour qu'elle soit suffisamment large pour accueillir la légende.

Cependant, cela peut également être automatisé:

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

entrez la description de l'image ici

jbaums
la source
L'utilisation de xpd = T ou xpd = NA n'empêche pas mon 'principal' (titre) d'être coupé lorsqu'il est étiré pour essayer d'utiliser la zone ajoutée avec la large marge droite.
Phil Goetz
@PhilGoetz êtes-vous sûr de tracer le principal dans la zone de tracé? Est-il possible que vous n'ayez pas assez de lignes de marge pour tracer?
jbaums
10

Récemment, j'ai trouvé une fonction très simple et intéressante pour imprimer la légende en dehors de la zone de tracé où vous le souhaitez.

Créez la marge extérieure sur le côté droit du tracé.

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

Créer un tracé

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

Ajoutez une légende et utilisez simplement la fonction locator (1) comme ci-dessous. Ensuite, vous devez simplement cliquer où vous voulez après le chargement du script suivant.

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

Essayez-le

Vandka
la source
9

Je ne peux offrir qu'un exemple de la solution de mise en page déjà indiquée.

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

une image moche: S

Roman Luštrik
la source
9

Ajout d'une autre alternative simple qui est assez élégante à mon avis.

Votre intrigue:

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

Légende:

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

Résultat:

photo avec légende

Ici, seule la deuxième ligne de la légende a été ajoutée à votre exemple. À son tour:

  • inset=c(0,1)- déplace la légende par fraction de zone de tracé dans les directions (x, y). Dans ce cas, la légende est en "bottomright"position. Il est déplacé par 0 région de traçage dans la direction x (donc reste à «droite») et par 1 région de traçage dans la direction y (de bas en haut). Et il se trouve qu'il apparaît juste au-dessus de l'intrigue.
  • xpd=TRUE - la légende apparaît en dehors de la région de traçage.
  • horiz=TRUE - demande de produire une légende horizontale.
  • bty="n" - un détail de style pour se débarrasser de la boîte englobante de légende.

Il en va de même lors de l'ajout d'une légende sur le côté:

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

Ici, nous avons simplement ajusté les positions des légendes et ajouté un espace de marge supplémentaire sur le côté droit du tracé. Résultat:

photo avec légende 2

Karolis Koncevičius
la source
4

Vous pouvez le faire avec l' API Plotly R , avec du code, ou à partir de l'interface graphique en faisant glisser la légende où vous le souhaitez.

Voici un exemple. Le graphique et le code sont également ici .

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)

Vous pouvez positionner la légende en dehors du graphique en affectant l'une des valeurs x et y à 100 ou -100.

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

Voici les autres options:

  • list("x" = 100, "y" = 0) pour l'extérieur en bas à droite
  • list("x" = 100, "y"= 1) Extérieur droit en haut
  • list("x" = 100, "y" = .5) Extérieur droit milieu
  • list("x" = 0, "y" = -100) Sous gauche
  • list("x" = 0.5, "y" = -100) Sous le centre
  • list("x" = 1, "y" = -100) Sous la droite

Puis la réponse.

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

Renvoie tracé une URL avec votre graphique lorsque vous passez un appel. Vous pouvez y accéder plus rapidement en appelant browseURL(response$url)pour qu'il ouvre votre graphique dans votre navigateur pour vous.

url = response$url
filename = response$filename

Cela nous donne ce graphique. Vous pouvez également déplacer la légende à partir de l'interface graphique, puis le graphique sera mis à l'échelle en conséquence. Divulgation complète: je fais partie de l'équipe Plotly.

Légende sur le côté du graphique

Mateo Sanchez
la source
2

Essayez layout()ce que j'ai utilisé pour cela dans le passé en créant simplement un tracé vide ci-dessous, correctement mis à l'échelle à environ 1/4 et en y plaçant manuellement les parties de la légende.

Il y a ici des questions plus anciennes sur legend()lesquelles devraient vous aider à démarrer.

Dirk Eddelbuettel
la source
Comme déjà dit dans la question, c'est ce à quoi j'ai pensé aussi. Mais ce serait idéal, s'il y avait un autre moyen. D'une manière ou d'une autre, je suppose qu'il n'y en a pas.
Henrik