2016-03-28 6 views
0

Es gibt ein Beispiel here für das Erstellen eines mehrfarbigen Texttitels.Matplotlib mehrfarbig Titel (Text) - in der Praxis

Allerdings möchte ich dies auf eine Handlung anwenden, die bereits eine Zahl enthält.

Zum Beispiel, wenn ich es auf diesen (gleichen Code wie bei dem Beispiel minus ein paar Extras und mit einer anderen Figur) ...:

plt.rcdefaults() 
import matplotlib.pyplot as plt 
%matplotlib inline 
from matplotlib import transforms 

fig = plt.figure(figsize=(4,3), dpi=300) 

def rainbow_text(x,y,ls,lc,**kw): 

    t = plt.gca().transData 
    fig = plt.gcf() 
    plt.show() 

    #horizontal version 
    for s,c in zip(ls,lc): 
     text = plt.text(x,y," "+s+" ",color=c, transform=t, **kw) 
     text.draw(fig.canvas.get_renderer()) 
     ex = text.get_window_extent() 
     t = transforms.offset_copy(text._transform, x=ex.width, units='dots') 

plt.figure() 
rainbow_text(0.5,0.5,"all unicorns poop rainbows ! ! !".split(), 
     ['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'], 
     size=40) 

... das Ergebnis ist 2 Parzellen mit der Titel vergrößert. Diese Art von macht für mich Sinn, weil ich plt verwende. zweimal. Aber wie integriere ich es so, dass es sich nur auf die erste Instanz von plt bezieht. beim Erstellen des Titels?

Auch darüber:

t = transforms.offset_copy(text._transform, x=ex.width, units='dots') 

Ich merke es, den Abstand zwischen den Wörtern verändern kann, aber wenn ich mit den Werten von x spielen, sind die Ergebnisse nicht vorhersagbar (Abstand ist inkonsistent zwischen den Wörtern). Wie kann ich diesen Wert sinnvoll anpassen?

Und schließlich, wo es heißt "units = 'dots'", was sind die anderen Optionen? Sind "Punkte" 1/72. eines Zolls (und ist das der Standard für Matplotlib?)?

Wie kann ich Einheiten von Punkten in Zoll konvertieren?

Vielen Dank im Voraus!

Antwort

1

In der Tat kommt die Bounding Box des Textes in Einheiten im Gegensatz zu denen, die zum Beispiel in Scatterplot verwendet werden. Text ist eine andere Art von Objekt, das irgendwie neu gezeichnet wird, wenn Sie die Größe des Fensters ändern oder das Verhältnis ändern. Durch ein stabilisiertes Fenster aufweisen, können Sie die Koordinaten des Begrenzungsrahmens in Plot Einheiten stellen und Ihre farbigen Text auf diese Weise bauen:

a = "all unicorns poop rainbows ! ! !".split() 
c = ['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'] 

f = plt.figure(figsize=(4,3), dpi=120) 
ax = f.add_subplot(111) 

r = f.canvas.get_renderer() 
space = 0.1 
w = 0.5 
counter = 0 
for i in a: 
    t = ax.text(w, 1.2, a[counter],color=c[counter],fontsize=12,ha='left') 
    transf = ax.transData.inverted() 
    bb = t.get_window_extent(renderer=f.canvas.renderer) 
    bb = bb.transformed(transf) 
    w = w + bb.xmax-bb.xmin + space 
    counter = counter + 1 
plt.ylim(0.5,2.5) 
plt.xlim(0.6,1.6) 
plt.show() 

, was zur Folge hat:

Matplotlib colored text

Dies ist jedoch , ist immer noch nicht ideal, da Sie die Größe Ihrer Diagrammachse steuern müssen, um die richtigen Abstände zwischen Wörtern zu erhalten. Dies ist etwas willkürlich, aber wenn Sie es schaffen, Ihr Programm mit einer solchen Kontrolle zu machen, ist es möglich, Plot-Einheiten zu verwenden, um Ihren beabsichtigten Zweck zu erreichen.

ORIGINAL POST:

plt. ist nur der Aufruf der Bibliothek. In Wirklichkeit erstellen Sie eine Instanz von plt.figure im globalen Gültigkeitsbereich (so kann es lokal in der Funktion gesehen werden). Aus diesem Grund überschreiben Sie die figure, weil Sie den gleichen Namen für die Variable verwenden (es ist also nur eine einzige Instanz am Ende). Um dies zu lösen, versuche die Namen deiner Figureninstanzen zu kontrollieren. Zum Beispiel:

import matplotlib.pyplot as plt 
#%matplotlib inline 
from matplotlib import transforms 

fig = plt.figure(figsize=(4,3), dpi=300) 
#plt.show(fig) 

def rainbow_text(x,y,ls,lc,**kw): 

    t = plt.gca().transData 
    figlocal = plt.gcf() 

    #horizontal version 
    for s,c in zip(ls,lc): 
     text = plt.text(x,y," "+s+" ",color=c, transform=t, **kw) 
     text.draw(figlocal.canvas.get_renderer()) 
     ex = text.get_window_extent() 
     t = transforms.offset_copy(text._transform, x=ex.width, units='dots') 

    plt.show(figlocal) #plt.show((figlocal,fig)) 

#plt.figure() 
rainbow_text(0.5,0.5,"all unicorns poop rainbows ! ! !".split(), 
     ['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'], 
     size=40,) 

Ich habe mehrere Anweisungen kommentiert aber bemerke ich einen anderen Namen geben für die figure lokal für die Funktion (figlocal). Beachte auch, dass ich in meinen Showbeispielen direkt kontrolliere, welche figure angezeigt werden soll.

Was Ihre anderen Fragen beachten Sie, andere Geräte verwenden können, wie in der Funktion Dokumentation zu sehen:

Return a new transform with an added offset. 
     args: 
     trans is any transform 
     kwargs: 
     fig is the current figure; it can be None if units are 'dots' 
     x, y give the offset 
     units is 'inches', 'points' or 'dots' 

EDIT: Anscheinend ist es eine Art von Problem mit den Ausmaßen des Begrenzungsrahmens für Text, der tut geben Sie nicht die richtige Breite des Wortes und somit ist der Abstand zwischen Wörtern nicht stabil. Ich rate dazu, die Latex-Funktionalität von Matplotlib zu verwenden, um die Farben in dieselbe Zeichenfolge zu schreiben (also nur einen Aufruf von plt.text). Sie können es so tun:

import matplotlib 
import matplotlib.pyplot as plt 
matplotlib.use('pgf') 
from matplotlib import rc 

rc('text',usetex=True) 
rc('text.latex', preamble=r'\usepackage{color}') 

a = "all unicorns poop rainbows ! ! !".split() 
c = ['red', 'orange', 'brown', 'green', 'blue', 'purple', 'black'] 
st = '' 
for i in range(len(a)): 
    st = st + r'\textcolor{'+c[i]+'}{'+a[i]+'}' 
plt.text(0.5,0.5,st) 
plt.show() 

Dies ist jedoch keine ideale Lösung. Der Grund ist, dass Sie Latex installiert haben müssen, einschließlich der notwendigen Pakete (beachten Sie, dass ich das Farbpaket verwende). Werfen Sie einen Blick auf Yann Antwort in dieser Frage:

+0

Danke, @armatita aber wie steuere ich (wirklich, regularisieren) die Breite für eine gegebene Einheit? Ich frage, weil diese Beispiele inkonsistente Abstände zwischen Wörtern erzeugen, was ich letztendlich zu beheben versuche (bezüglich des zweiten Teils meiner Frage). –

+1

@Dance Party2 Wenn diese Transformation nicht den richtigen Weg macht, vergiss einfach die Transformation. Definieren Sie Ihre Schriftart, Ausrichtung und plotten Sie jedes Wort mit Koordinaten. Beachten Sie, dass Sie bereits die Bounding-Box des Textes erhalten (text.get_window_extent()), so dass Sie z. B. sagen können, dass ein Leerzeichen 0,2 bedeutet, dass Word2 in Word1_Width + 0,2 positioniert sein soll (und so weiter für Word3) wird sich selbst eine Art Transformation aufbauen. Die Sache ist, ich bin mir nicht sicher, was diese Transformation, die Sie verwenden, tatsächlich tut. Auch: http://stackoverflow.com/questions/5320205/matplotlib-text-dimensions – armatita

+0

Ja! Ich danke dir sehr! Das wird mir Stunden Zeit sparen. Wenn du das als Antwort posten willst, nehme ich es gerne an. –

0

@armatita: Ich denke, Ihre Antwort tatsächlich tut, was ich brauche. Ich dachte, ich brauche stattdessen Anzeigekoordinaten, aber es sieht so aus, als könnte ich einfach die Koordinaten der Achse 1 verwenden, wenn es das ist (ich plane die Verwendung mehrerer Achsen über subplot2grid). Hier ein Beispiel:

import matplotlib.pyplot as plt 
%matplotlib inline 
dpi=300 
f_width=4 
f_height=3 
f = plt.figure(figsize=(f_width,f_height), dpi=dpi) 

ax1 = plt.subplot2grid((100,115), (0,0), rowspan=95, colspan=25) 
ax2 = plt.subplot2grid((100,115), (0,30), rowspan=95, colspan=20) 
ax3 = plt.subplot2grid((100,115), (0,55), rowspan=95, colspan=35) 
ax4 = plt.subplot2grid((100,115), (0,95), rowspan=95, colspan=20) 
r = f.canvas.get_renderer() 
t = ax1.text(.5, 1.1, 'a lot of text here',fontsize=12,ha='left') 
space=0.1 
w=.5 

transf = ax1.transData.inverted() 
bb = t.get_window_extent(renderer=f.canvas.renderer) 
bb = bb.transformed(transf) 

e = ax1.text(.5+bb.width+space, 1.1, 'text',fontsize=12,ha='left') 

print(bb) 
plt.show() 

Ich bin nicht sicher, was du meinst über die Achsengröße zu steuern, though. Beziehen Sie sich darauf, den Code in verschiedenen Umgebungen zu verwenden oder das Bild in verschiedenen Größen zu exportieren? Ich habe vor, das Bild in der gleichen Umgebung und in der gleichen Größe zu verwenden (pro Instanz der Verwendung dieses Ansatzes), also denke ich, dass es in Ordnung sein wird. Macht meine Logik Sinn? Ich habe ein schwaches Verständnis davon, was wirklich vor sich geht, also hoffe ich es. Ich würde es mit einer Funktion verwenden (durch Teilen des Textes), wie Sie es getan haben, aber es gibt Fälle, in denen ich auf andere Zeichen aufteilen muss (d. H. Wenn ein Wort in Klammern gefärbt sein sollte, aber nicht die Klammern). Vielleicht kann ich einfach ein Trennzeichen wie ',' einfügen? Ich denke, ich brauche eine andere Form von .split(), weil es nicht funktionierte, als ich es versuchte. Wenn ich das in all meinen Charts umsetzen kann, wird es mir auf jeden Fall unzählige Stunden ersparen. Ich danke dir sehr! Hier

+1

Gern geschehen. Sie haben wahrscheinlich bemerkt, dass die Achse neu gezeichnet wird, wenn Sie die Größe eines Matplolib-Diagramms ändern. Diese Umwandlung folgt nicht für Text (oder tut es, aber nicht in einer Weise, die für Sie nützlich ist). Wenn Sie also einen Plot (mit der für Sie passenden Anpassung) erstellen und dann versuchen, die Größe zu ändern, verlieren Sie die wertvollen gleichen Leerräume, die Sie zu erhalten versuchen. Dies ist der Grund, warum ich denke, es ist keine ideale Lösung, aber wenn Sie absolute Kontrolle über die Größe Ihrer Plots haben, als es den Trick tun sollte. – armatita

+0

Das ist in Ordnung. Ich denke, ich verstehe was du meinst. Wenn ich statt 4 die Breite 10 Zoll mache, erhöht sich der Abstand (den ich auf 0,1 setze). Für einen gegebenen Plot mit einer gegebenen Breite wird es nicht viel für mich sein, diesen Wert anzupassen, bis ich etwas bekomme, das funktioniert. –

+0

Ich habe festgestellt, dass, wenn ich eine Zeichenfolge auf Kommas wie diese aufteilen muss, ich mich nicht um Abstände kümmern muss, weil ich einfach die Leerzeichen zwischen Trennzeichen einfügen kann: a = str ("Gruppe 1, vs. Gruppe 2 (, sub1,) und (, sub2,) "). split (',') ... Farben: c = ['schwarz', 'rot', 'schwarz', 'grün', 'schwarz', 'blau', 'schwarz '] –

0

ist ein Beispiel, wo es 2 Stellplätze und 2 Instanzen mit der Funktion für die Nachwelt:

import matplotlib.pyplot as plt 
%matplotlib inline 
dpi=300 
f_width=4 
f_height=3 
f = plt.figure(figsize=(f_width,f_height), dpi=dpi) 

ax1 = plt.subplot2grid((100,60), (0,0), rowspan=95, colspan=30) 
ax2 = plt.subplot2grid((100,60), (0,30), rowspan=95, colspan=30) 

f=f #Name for figure 
string = str("Group 1 ,vs. ,Group 2 (,sub1,) and (,sub2,)").split(',') 
color = ['black','red','black','green','black','blue','black'] 
xpos = .5 
ypos = 1.2 
axis=ax1 
#No need to include space if incuded between delimiters above 
#space = 0.1 
def colortext(f,string,color,xpos,ypos,axis): 
#f=figure object name (i.e. fig, f, figure) 
    r = f.canvas.get_renderer() 
    counter = 0 
    for i in string: 
     t = axis.text(xpos, ypos, string[counter],color=color[counter],fontsize=12,ha='left') 
     transf = axis.transData.inverted() 
     bb = t.get_window_extent(renderer=f.canvas.renderer) 
     bb = bb.transformed(transf) 
     xpos = xpos + bb.xmax-bb.xmin 
     counter = counter + 1 
colortext(f,string,color,xpos,ypos,axis) 

string2 = str("Group 1 part 2 ,vs. ,Group 2 (,sub1,) and (,sub2,)").split(',') 
ypos2=1.1 
colortext(f,string2,color,xpos,ypos2,axis) 

plt.show()