Eine Legende außerhalb des Plotbereichs in Basisgrafiken zeichnen?

185

Wie der Titel schon sagt: Wie kann ich eine Legende außerhalb des Plotbereichs zeichnen, wenn ich Basisgrafiken verwende?

Ich habe darüber nachgedacht, mit layouteinem leeren Plot herumzuspielen und ihn zu erstellen, um nur die Legende zu enthalten, aber ich wäre daran interessiert, nur die Basisgraph-Funktionen zu verwenden und z. B. par(mar = )rechts neben dem Plot Platz für die Legende zu schaffen.


Hier ein Beispiel:

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

produziert:

Alt-Text

Wie gesagt, ich möchte, dass sich die Legende außerhalb des Plotbereichs befindet (z. B. rechts neben dem Diagramm / Plot.

Henrik
quelle
... Sie können auch gleich mit Dummy-Container für die Legende hacken, einfach und recht bequem von Zeit zu Zeit. Ähnliche Frage hier .
hhh
2
@hhh Der Link funktioniert nicht mehr. Können Sie es mit diesem Ansatz aktualisieren oder eine Antwort veröffentlichen?
Henrik

Antworten:

111

Möglicherweise müssen Sie par(xpd=TRUE)ermöglichen, dass Dinge außerhalb des Plotbereichs gezeichnet werden. Wenn Sie also die Haupthandlung mit machen, haben bty='L'Sie rechts Platz für eine Legende. Normalerweise wird dies auf den Plotbereich beschränkt, aber par(xpd=TRUE)mit ein wenig Anpassung können Sie eine Legende so weit wie möglich nach rechts bringen:

 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
quelle
33
Beachten Sie, dass Sie xpd direkt an die Legende übergeben können, damit Sie sich danach keine Gedanken mehr über das Zurücksetzen von par machen müssen. In grconvertX & Y finden Sie auch eine Möglichkeit, die Position der Legende so anzugeben, dass sie nicht von den Grenzen der Daten abhängt, die Sie zeichnen.
Charles
6
Da diese Frage und Antwort immer noch sehr beliebt sind, par(xpd=NA)ist sie noch mächtiger (dh Handlungen in mehr Regionen).
Henrik
+1. Wir sollten erwähnen, dass es sinnvoll ist, parkurz vor der Legende einen separaten Anruf zu tätigen. In meiner Handlung habe ich bei par(new=T)mehreren anderen Gelegenheiten verwendet und wollte einfach den xpdParameter im selben Aufruf hinzufügen , was zu Problemen führt.
Matt Bannert
146

Niemand hat erwähnt, negative insetWerte für zu verwenden legend. Hier ist ein Beispiel, in dem sich die Legende rechts neben dem Plot befindet und oben ausgerichtet ist (mithilfe des Schlüsselworts "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")

Der erste Wert von muss inset=c(-0.2,0)möglicherweise basierend auf der Breite der Legende angepasst werden.

legend_right

Mike T.
quelle
14
@ Henrik nein es geht nicht ohne xpd = TRUE. Beachten Sie auch, dass es besser ist, xpd = TRUE als Argument für die Funktion legend () festzulegen.
Stéphane Laurent
1
Manchmal xpdmuss eingestellt werden, TRUEdamit der negative Einschub funktioniert. Aber manchmal nicht. Mit dem Befehl args.legend=list(x="bottom", horiz=TRUE, inset=-0.2)innerhalb eines barplot(...scheint es nicht zu brauchen, xpd=TRUEaber mit nur legend(x="bottom", horiz=TRUE, inset=-0.2)scheint es zu brauchen xpd=TRUE. Irgendwelche Einsichten? Ich bin nur verwirrt, wenn ich meine Argumente weitergebe?
user3386170
28

Eine andere Lösung neben den bereits erwähnten Ondes (mit layoutoderpar(xpd=TRUE) ) besteht darin, Ihr Diagramm mit einem transparenten Diagramm über das gesamte Gerät zu legen und dann die Legende hinzuzufügen.

Der Trick besteht darin, ein (leeres) Diagramm über den gesamten Plotbereich zu legen und die Legende hinzuzufügen. Wir können die par(fig=...)Option nutzen. Zuerst weisen wir R an, ein neues Diagramm über das gesamte Zeichengerät zu erstellen:

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

Einstellung omaund marist erforderlich, da das Innere des Grundstücks das gesamte Gerät abdecken soll. new=TRUEwird benötigt, um zu verhindern, dass R ein neues Gerät startet. Wir können dann das leere Diagramm hinzufügen:

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

Und wir sind bereit, die Legende hinzuzufügen:

legend("bottomright", ...)

fügt unten rechts auf dem Gerät eine Legende hinzu. Ebenso können wir die Legende am oberen oder rechten Rand hinzufügen. Das einzige, was wir sicherstellen müssen, ist, dass der Rand des ursprünglichen Grundstücks groß genug ist, um die Legende aufzunehmen.

All dies in eine Funktion umsetzen;

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

Und ein Beispiel. Erstellen Sie zuerst das Diagramm und stellen Sie sicher, dass unten genügend Platz vorhanden ist, um die Legende hinzuzufügen:

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

Fügen Sie dann die Legende hinzu

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

Ergebend:

Beispielfigur zeigt Legende am oberen Rand

Jan van der Laan
quelle
2
Tolle Ergänzung zur Liste hier. In der Grafik finden Sie hier eine Erklärung, wie dies mit mehreren Plots funktioniert .
Shiri
Jan, gibt es eine Möglichkeit, die Schriftgröße in der Legende zu erhöhen, ohne dass Text abgeschnitten wird? Zum Beispiel habe ich eine Grafik 4 verschiedene Arten von Etiketten, aber mit viel Leerraum zwischen ihnen.
Ein alter Mann im Meer.
Ich habe eine Frage mit mehr Details geschrieben stackoverflow.com/questions/42707308/…
Ein alter Mann im Meer.
16

Es tut mir leid, dass ich einen alten Thread wiederbelebt habe, aber ich hatte heute das gleiche Problem. Der einfachste Weg, den ich gefunden habe, ist der folgende:

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

Hier zu finden: http://www.harding.edu/fmccown/R/

veiga
quelle
4
Noch besser ist oldpar <- par (xpd = T, mar = par () $ mar + c (0,0,0,6)) ... par (oldpar) (Siehe die Hilfe von par)
rakensi
Diese Lösung ist besser, da der Platz für die Legende unabhängig von der Länge der Zeichenfolgen der Legende festgelegt ist
Sergio
15

Ich mache es gerne so:

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

Geben Sie hier die Bildbeschreibung ein

Die einzige erforderliche Optimierung besteht darin, den rechten Rand so einzustellen, dass er der Legende entspricht.

Dies kann jedoch auch automatisiert werden:

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

Geben Sie hier die Bildbeschreibung ein

jbaums
quelle
Die Verwendung von xpd = T oder xpd = NA verhindert nicht, dass mein 'main' (Titel) abgeschnitten wird, wenn er gedehnt wird, um zu versuchen, den mit dem breiten rechten Rand hinzugefügten Bereich zu verwenden.
Phil Goetz
@PhilGoetz Sind Sie sicher, dass Sie Main innerhalb des Plotbereichs zeichnen? Ist es möglich, dass Sie dort nicht genügend Randlinien haben, um zu zeichnen?
Jbaums
10

Kürzlich fand ich eine sehr einfache und interessante Funktion, um Legenden außerhalb des gewünschten Plotbereichs zu drucken.

Machen Sie den äußeren Rand auf der rechten Seite des Diagramms.

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

Erstellen Sie ein Diagramm

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

Fügen Sie eine Legende hinzu und verwenden Sie einfach die Locator (1) -Funktion wie unten. Dann müssen Sie nur noch auf die gewünschte Stelle klicken, nachdem Sie das folgende Skript geladen haben.

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

Versuch es

Vandka
quelle
9

Ich kann nur ein Beispiel für die bereits erwähnte Layoutlösung anbieten.

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

ein hässliches Bild: S.

Roman Luštrik
quelle
9

Hinzufügen einer weiteren einfachen Alternative, die meiner Meinung nach recht elegant ist.

Ihr Grundstück:

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

Legende:

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

Ergebnis:

Bild mit Legende

Hier wurde Ihrem Beispiel nur die zweite Zeile der Legende hinzugefügt. Im Gegenzug:

  • inset=c(0,1)- Verschiebt die Legende um einen Bruchteil des Plotbereichs in (x, y) Richtungen. In diesem Fall ist die Legende bei"bottomright" Position. Es wird um 0 Zeichenbereiche in x-Richtung (bleibt also "rechts") und um 1 Zeichenbereich in y-Richtung (von unten nach oben) verschoben. Und so kommt es, dass es direkt über der Handlung erscheint.
  • xpd=TRUE - Lassen Sie die Legende außerhalb des Plotbereichs erscheinen.
  • horiz=TRUE - weist an, eine horizontale Legende zu erstellen.
  • bty="n" - Ein Stildetail, um den Legendenbegrenzungsrahmen loszuwerden.

Gleiches gilt beim Hinzufügen einer Legende zur Seite:

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

Hier haben wir einfach die Legendenpositionen angepasst und zusätzlichen Randbereich auf der rechten Seite des Diagramms hinzugefügt. Ergebnis:

Bild mit Legende 2

Karolis Koncevičius
quelle
4

Sie können dies mit der Plotly R-API entweder mit Code oder über die GUI tun , indem Sie die Legende an die gewünschte Stelle ziehen.

Hier ist ein Beispiel. Das Diagramm und der Code sind auch hier .

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)

Sie können die Legende außerhalb des Diagramms positionieren, indem Sie 100 oder -100 einen der x- und y-Werte zuweisen.

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

Hier sind die anderen Optionen:

  • list("x" = 100, "y" = 0) für außen rechts unten
  • list("x" = 100, "y"= 1) Außerhalb rechts oben
  • list("x" = 100, "y" = .5) Außerhalb rechts Mitte
  • list("x" = 0, "y" = -100) Unter links
  • list("x" = 0.5, "y" = -100) Unter Mitte
  • list("x" = 1, "y" = -100) Unter rechts

Dann die Antwort.

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

Plotly gibt eine URL mit Ihrem Diagramm zurück, wenn Sie einen Anruf tätigen. Sie können schneller darauf zugreifen, indem Sie anrufen, browseURL(response$url)damit Ihr Diagramm in Ihrem Browser für Sie geöffnet wird.

url = response$url
filename = response$filename

Das gibt uns diese Grafik. Sie können die Legende auch aus der GUI heraus verschieben, und das Diagramm wird entsprechend skaliert. Vollständige Offenlegung: Ich bin im Plotly-Team.

Legende auf der Seite des Diagramms

Mateo Sanchez
quelle
2

Versuchen Sie, layout()was ich in der Vergangenheit dafür verwendet habe, indem Sie einfach ein leeres Diagramm unten erstellen, das richtig auf ungefähr 1/4 skaliert ist, und die Legendenteile manuell darin platzieren.

Hier gibt es einige ältere Fragen, mit legend()denen Sie beginnen sollten.

Dirk Eddelbuettel
quelle
Wie bereits in der Frage erwähnt, habe ich auch darüber nachgedacht. Aber es wäre ideal, wenn es einen anderen Weg gäbe. Irgendwie gibt es wohl keine.
Henrik