wake-up-neo.com

matplotlib: Ausrichtung der y-Achsenbeschriftungen in gestapelten Streudiagrammen

Im untenstehenden Diagramm gibt es zwei Streudiagramme mit unterschiedlicher Skalierung, sodass die Y-Achsenbeschriftungen nicht ausgerichtet sind. Gibt es eine Möglichkeit, die horizontale Ausrichtung in den Beschriftungen der y-Achse zu erzwingen?

import matplotlib.pylab as plt
import random
import matplotlib.gridspec as gridspec

random.seed(20)
data1 = [random.random() for i in range(10)]
data2 = [random.random()*1000 for i in range(10)]

gs = gridspec.GridSpec(2,1)
fig = plt.figure()

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size =16)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size =16)

plt.show()

stacked scatter plots

22
dimka

Sie können die set_label_coords-Methode verwenden.

import matplotlib.pylab as plt
import random
import matplotlib.gridspec as gridspec

random.seed(20)
data1 = [random.random() for i in range(10)]
data2 = [random.random()*1000 for i in range(10)]

gs = gridspec.GridSpec(2,1)
fig = plt.figure()

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size =16)
ax.get_yaxis().set_label_coords(-0.1,0.5)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size =16)
ax.get_yaxis().set_label_coords(-0.1,0.5)

enter image description here

25
Greg Whittier

Wie im Kommentar angegeben, wird das, was Sie suchen, mit set_label_coords() wie hier beschrieben - gelöst. Für Ihren Fall wird es ungefähr so ​​sein:

labelx = -0.5

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size=16)
ax.yaxis.set_label_coords(labelx, 0.5)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size=16)
ax.yaxis.set_label_coords(labelx, 0.5)
3

Seit dem Schreiben dieser Frage hat matplotlib eine einfach zu verwendende Funktion hinzugefügt, mit der die Etiketten ausgerichtet werden. Um die Ausrichtung der Etiketten zu erzwingen, verwenden Sie die Funktion fig.align_labels(), bevor Sie die Abbildung anzeigen.

Wenn Sie eine detailliertere Steuerung benötigen, können Sie auch die Funktionen Figure.align_xlabels() oder Figure.align_ylabels() verwenden.

Hier ist eine funktionierende Version des Codes, der in der Frage veröffentlicht wurde. Es wurde nur eine Zeile hinzugefügt (die vorletzte Zeile), um die Lösung zu implementieren.

import matplotlib.pylab as plt
import random
import matplotlib.gridspec as gridspec

random.seed(20)
data1 = [random.random() for i in range(10)]
data2 = [random.random()*1000 for i in range(10)]

gs = gridspec.GridSpec(2,1)
fig = plt.figure()

ax = fig.add_subplot(gs[0])
ax.plot(data1)
ax.set_ylabel(r'Label One', size =16)

ax = fig.add_subplot(gs[1])
ax.plot(data2)
ax.set_ylabel(r'Label Two', size =16)

fig.align_labels()
plt.show()

Weitere Informationen finden Sie in der Matplotlib-Dokumentation zu Ausrichtungsetiketten .

3
Steven Noyce

Hier ist eine Funktion, die ich für das automatische Ausrichten der Beschriftungen geschrieben habe, aber es scheint nicht interaktiv in einem Skript zu funktionieren.

def align_labels(axes_list,axis='y',align=None):
    if align is None:
        align = 'l' if axis == 'y' else 'b'
    yx,xy = [],[]
    for ax in axes_list:
        yx.append(ax.yaxis.label.get_position()[0])
        xy.append(ax.xaxis.label.get_position()[1])

    if axis == 'x':
        if align in ('t','top'):
            lim = max(xy)
        Elif align in ('b','bottom'):
            lim = min(xy)
    else:
        if align in ('l','left'):
            lim = min(yx)
        Elif align in ('r','right'):
            lim = max(yx)

    if align in ('t','b','top','bottom'):
        for ax in axes_list:
            t = ax.xaxis.label.get_transform()
            x,y = ax.xaxis.label.get_position()
            ax.xaxis.set_label_coords(x,lim,t)
    else:
        for ax in axes_list:
            t = ax.yaxis.label.get_transform()
            x,y = ax.yaxis.label.get_position()
            ax.yaxis.set_label_coords(lim,y,t)

Und ein Beispiel:

fig,ax = subplots(2,2)
ax00,ax01 = ax[0]
ax10,ax11 = ax[1]
ax00.set_ylim(1000,5000)
ax00.set_ylabel('top')
ax10.set_ylabel('bottom')
ax10.set_xlabel('left')
ax11.set_xlabel('right')
ax11.xaxis.axis_date()
fig.autofmt_xdate()
#we have to call draw() so that matplotlib will figure out the automatic positions
fig.canvas.draw()
align_labels(ax[:,0],'y')
align_labels(ax[1],'x')

 example figure

1
Samuel Powell

Ich gebe am Ende eine Lösung, aber zuerst sage ich, welcher Weg nicht zum Erfolg führt.

Ich habe dieses Thema kürzlich wieder aufgegriffen und einige Zeit mit dem Ausprobieren verschiedener Lösungen verbracht, d. H. Fast alle möglichen Kombinationen von Transformationen zwischen den verschiedenen Koordinatensystemen und deren Beziehung zur tight_layout(). Ich habe nur mit backend_pdf experimentiert, daher kann ich nicht über interaktive Medien berichten. Kurz gesagt, meine Schlussfolgerung lautet: Egal wie Sie versuchen, die Positionen herauszufinden und sie zu transformieren, es ist auf dieser Ebene nicht möglich, die Achsenbeschriftungen auszurichten. Ich denke, irgendwie sollte es möglich sein, zum Beispiel irgendwie, dass Matplotlib die Achsen der Subplots selbst ausrichten kann. aber immer noch nicht ausgerichtet:

# sorry for the `self`, this is from a class
def align_x_labels(self):
    self.lowest_ax = min(self.axes.values(),
                         key = lambda ax: ax.xaxis.label.get_position()[1])
    self.lowest_xlab_dcoo = self.lowest_ax.transData.transform(
        self.lowest_ax.xaxis.label.get_position())
    list(
        map(
                lambda ax: \
                    ax.xaxis.set_label_coords(
                        self.fig.transFigure.inverted().transform(
                            ax.transAxes.transform((0.5, 0.5)))[0],
                        self.fig.transFigure.inverted().transform(
                            self.lowest_xlab_dcoo)[1],
                        transform = self.fig.transFigure
                    ),
                self.axes.values()
            )
    )

Es ist eine Schande, dass eine solche Grundfunktionalität nicht erreicht werden kann, und es ist unklar, wie die verschiedenen Koordinatenräume bei den verschiedenen Plotschritten transformiert und neu skaliert werden. Ich würde mich sehr über eine klare Erklärung freuen, denn die matplotlib-Webseite umreißt nur die Architektur, stellt einfache Fälle vor, erläutert jedoch solche Situationen nicht. Ich bin auch überrascht, dass Methoden, die Koordinaten akzeptieren oder zurückgeben, in ihrem Dokument nicht angeben, welche Arten von Koordinaten diese sind. Schließlich fand ich sehr nützlich dieses Tutorial .

Lösung

Am Ende habe ich, anstatt mich mit den Transformationen zu beschäftigen, in der Variablen GridSpec eine zusätzliche Zeile mit Nullhöhe und unsichtbaren Achsen erstellt (dasselbe kann mit einer Nullbreite-Spalte für Y-Achsenbeschriftungen geschehen). Dann fügte ich Beschriftungen für diese Unterzeichnungen hinzu, und verticalalignment wurde auf top gesetzt. 

# get one of the zero height phantom subplots to `self.ax`:
self.get_subplot(i, 1)
# set empty ticklabels:
self.ax.xaxis.set_ticklabels([])
self.ax.yaxis.set_ticklabels([])
# set the axis label:
self.ax.set_xlabel(labtext, fontproperties = self.fp_axis_lab)
# and this is matter of aesthetics
# sometimes bottom or center might look better:
self.ax.xaxis.label.set_verticalalignment('top')
0
deeenes