2016-07-28 28 views
10

Ich möchte eine komplexe Legende in Matplotlib machen. Ich habe den folgenden CodeTabellenlegende in Matplotlib

import matplotlib.pylab as plt 
import numpy as np 

N = 25 
y = np.random.randn(N) 
x = np.arange(N) 

y2 = np.random.randn(25) 

# serie A 
p1a, = plt.plot(x, y,  "ro", ms=10, mfc="r", mew=2, mec="r") 
p1b, = plt.plot(x[:5], y[:5] , "w+", ms=10, mec="w", mew=2) 
p1c, = plt.plot(x[5:10], y[5:10], "w*", ms=10, mec="w", mew=2) 

# serie B 
p2a, = plt.plot(x, y2,  "bo", ms=10, mfc="b", mew=2, mec="b") 
p2b, = plt.plot(x[15:20], y2[15:20] , "w+", ms=10, mec="w", mew=2) 
p2c, = plt.plot(x[10:15], y2[10:15], "w*", ms=10, mec="w", mew=2) 


plt.legend([p1a, p2a, (p1a, p1b), (p2a,p2b), (p1a, p1c), (p2a,p2c)], 
["No prop", "No prop", "Prop +", "Prop +", "Prop *", "Prop *"], ncol=3, numpoints=1) 

plt.show() 

Es produziert Grundstück wie folgt aus: enter image description here

Aber ich möchte komplexe Legende plotten wie hier:

enter image description here

Ich versuchte auch, die zu tun Legende mit table Funktion, aber ich kann nicht ein Patch-Objekt in die Tabelle zu einer richtigen Position einer Zelle.

+0

ich nicht positiv bin, aber ich glaube, es ist ein Beispiel genau, dass in der akzeptierte Antwort tun [diese] (http : //stackoverflow.com/questions/21570007/custom-legend-in-matplotlib) Frage. Oder kann es dich wenigstens in die richtige Richtung weisen? – Ajean

+0

Nein, in diesem Beispiel hat jeder Marker sein eigenes Label. – Serenity

+0

Richtig, aber Sie können dort leere Strings setzen. Ich war eigentlich auf der Suche nach einem anderen Beispiel, das ich hier schon mal gesehen habe (jemand hat eine Legende geschrieben, die etwas Schönes war), aber ich konnte es nicht aufspüren. Nur ein Gedanke, weil ich denke, dass man leere Saiten benutzt. Tut mir leid, ich kann es nicht finden ... – Ajean

Antwort

3

Ist diese Lösung nahe genug, um Ihren Wünschen? Es ist leicht von Ricardos Antwort inspiriert, aber ich habe nur ein Legendenobjekt für jede Spalte verwendet und dann das title -Keyword verwendet, um den Titel jeder einzelnen Spalte festzulegen. Um die Markierungen in die Mitte jeder Spalte zu setzen, habe ich handletextpad mit einem negativen Wert verwendet, um es rückwärts zu schieben. Zu einzelnen Zeilen gibt es keine Legenden. Ich musste auch einige Leerzeichen in die Titel-Strings einfügen, damit sie auf dem Bildschirm gleich groß aussehen.

Ich merkte auch jetzt, als die Figur gespeichert wurde, dass zusätzliche Änderungen an der genauen Position der Legendenboxen erforderlich sind, aber da ich denke, dass Sie vielleicht noch mehr Sachen im Code optimieren wollen, überlasse ich es Ihnen. Sie müssen möglicherweise auch selbst mit dem handletextpad spielen, um sie "perfekt" ausgerichtet zu machen.

import matplotlib.pylab as plt 
import numpy as np 
plt.close('all') 

N = 25 
y = np.random.randn(N) 
x = np.arange(N) 

y2 = np.random.randn(25) 

# serie A 
p1a, = plt.plot(x, y,  "ro", ms=10, mfc="r", mew=2, mec="r") 
p1b, = plt.plot(x[:5], y[:5] , "w+", ms=10, mec="w", mew=2) 
p1c, = plt.plot(x[5:10], y[5:10], "w*", ms=10, mec="w", mew=2) 

# serie B 
p2a, = plt.plot(x, y2,  "bo", ms=10, mfc="b", mew=2, mec="b") 
p2b, = plt.plot(x[15:20], y2[15:20] , "w+", ms=10, mec="w", mew=2) 
p2c, = plt.plot(x[10:15], y2[10:15], "w*", ms=10, mec="w", mew=2) 

line_columns = [ 
       p1a, p2a, 
       (p1a, p1b), (p2a, p2b), 
       (p1a, p1c), (p2a, p2c) 
       ] 


leg1 = plt.legend(line_columns[0:2], ['', ''], ncol=1, numpoints=1, 
        title='No prop', handletextpad=-0.4, 
        bbox_to_anchor=[0.738, 1.]) 
leg2 = plt.legend(line_columns[2:4], ['', ''], ncol=1, numpoints=1, 
        title=' Prop + ', handletextpad=-0.4, 
        bbox_to_anchor=[0.87, 1.]) 
leg3 = plt.legend(line_columns[4:6], ['', ''], ncol=1, numpoints=1, 
        title=' Prop * ', handletextpad=-0.4, 
        bbox_to_anchor=[0.99, 1.]) 

plt.gca().add_artist(leg1) 
plt.gca().add_artist(leg2) 
plt.gca().add_artist(leg3) 

plt.gcf().show() 

enter image description here

bearbeiten

Vielleicht funktioniert das besser. Du musst noch ein paar Sachen optimieren, aber das Alignment-Problem der Bboxen ist weg.

leg = plt.legend(line_columns, ['']*len(line_columns), 
      title='No Prop Prop + Prop *', 
      ncol=3, numpoints=1, handletextpad=-0.5) 

enter image description here

+0

Es ist schwer, diese Legende richtig zu setzen, weil Anker nicht automatisch berechnen – Serenity

+0

Ja ein notiert, dass auch ... Bitte sehen Sie meine bearbeitete Antwort. Anstatt mehrere Labels zu verwenden, habe ich alles durch ein Label ersetzt. Das einzige Optimieren, das jetzt gemacht werden muss, ist das Spielen mit dem internen Abstand des 'title' und des' handletextpad'-Wertes. Ist das besser? – pathoren

+0

Sehr gut, sehr schön. Vielen Dank. – Serenity

2

Es scheint, dass es keinen Standardansatz dafür gibt, anstatt einige Tricks, die hier verfügbar sind.

Es ist erwähnenswert, dass Sie die Größe bbox Faktor überprüfen sollten, die Ihnen am besten passt.

Das Beste, was ich bisher finden konnte, vielleicht ein können Sie zu einer besseren Lösung führen:

N = 25 
y = np.random.randn(N) 
x = np.arange(N) 

y2 = np.random.randn(25) 

# Get current size 
fig_size = list(plt.rcParams["figure.figsize"]) 

# Set figure width to 12 and height to 9 
fig_size[0] = 12 
fig_size[1] = 12 
plt.rcParams["figure.figsize"] = fig_size 

# serie A 
p1a, = plt.plot(x, y,  "ro", ms=10, mfc="r", mew=2, mec="r") 
p1b, = plt.plot(x[:5], y[:5] , "w+", ms=10, mec="w", mew=2) 
p1c, = plt.plot(x[5:10], y[5:10], "w*", ms=10, mec="w", mew=2) 

# serie B 
p2a, = plt.plot(x, y2,  "bo", ms=10, mfc="b", mew=2, mec="b") 
p2b, = plt.plot(x[15:20], y2[15:20] , "w+", ms=10, mec="w", mew=2) 
p2c, = plt.plot(x[10:15], y2[10:15], "w*", ms=10, mec="w", mew=2) 

v_factor = 1. 
h_factor = 1. 

leg1 = plt.legend([(p1a, p1a)], ["No prop"], bbox_to_anchor=[0.78*h_factor, 1.*v_factor]) 
leg2 = plt.legend([(p2a, p2a)], ["No prop"], bbox_to_anchor=[0.78*h_factor, .966*v_factor]) 

leg3 = plt.legend([(p2a,p2b)], ["Prop +"], bbox_to_anchor=[0.9*h_factor, 1*v_factor]) 
leg4 = plt.legend([(p1a, p1b)], ["Prop +"], bbox_to_anchor=[0.9*h_factor, .966*v_factor]) 

leg5 = plt.legend([(p1a, p1c)], ["Prop *"], bbox_to_anchor=[1.*h_factor, 1.*v_factor]) 
leg6 = plt.legend([(p2a,p2c)], ["Prop *"], bbox_to_anchor=[1.*h_factor, .966*v_factor]) 

plt.gca().add_artist(leg1) 
plt.gca().add_artist(leg2) 
plt.gca().add_artist(leg3) 
plt.gca().add_artist(leg4) 
plt.gca().add_artist(leg5) 
plt.gca().add_artist(leg6) 
plt.show() 

enter image description here

1

I zu verbessern, die Antwort von @pathoren zur Position Legenden automatisch in einem Zyklus entsprechend Bbox der Legenden koordinieren. Dieser Code ermöglicht alle gewünschten Grenzlinien eines komplexen Legende zeigen:

import matplotlib.pylab as plt 
import numpy as np 
plt.close('all') 

# test data 
N = 25 
y = np.random.randn(N) 
x = np.arange(N) 
y2 = np.random.randn(25) 

# serie A 
p1a, = plt.plot(x, y, "ro", ms=10, mfc="r", mew=2, mec="r") 
p1b, = plt.plot(x[:5], y[:5], "w+", ms=10, mec="w", mew=2) 
p1c, = plt.plot(x[5:10], y[5:10], "w*", ms=10, mec="w", mew=2) 
# serie B 
p2a, = plt.plot(x, y2, "bo", ms=10, mfc="b", mew=2, mec="b") 
p2b, = plt.plot(x[15:20], y2[15:20], "w+", ms=10, mec="w", mew=2) 
p2c, = plt.plot(x[10:15], y2[10:15], "w*", ms=10, mec="w", mew=2) 

# legend handlers 
columns = [p1a, p2a, 
(p1a, p1b), (p2a, p2b), 
(p1a, p1c), (p2a, p2c)] 

ax = plt.gca() 
fig = plt.gcf() 
legs = [] 
# set the first legend in desired position 
leg = plt.legend(columns[0:2], ['', ''], ncol=1, numpoints=1, 
borderaxespad=0., title='No prop.', framealpha=.75, 
facecolor='w', edgecolor='k', loc=2, fancybox=None) 
ax.add_artist(leg) 
fig.canvas.draw() 
plt.pause(1.e-3) 

# get bbox postion of 1st legend to calculate 
# postion of 2nd and 3rd legends according to loc 
for i,si in enumerate(['+','*']): 
    bbox = leg.get_window_extent().inverse_transformed(ax.transAxes) 
    # next legends 
    leg = plt.legend(columns[(i+1)*2:(i+1)*2+2], ['', ''], ncol=1, numpoints=1, 
    title='Prop. '+si, framealpha=.75, borderaxespad=.0, 
    bbox_to_anchor=(bbox.x1-bbox.height*.08, bbox.y0, bbox.width, bbox.height), 
    facecolor='w', edgecolor='k') 
    ax.add_artist(leg) 
    fig.canvas.draw() 
    plt.pause(1.e-3) 

plt.show() 

enter image description here