Zeilen- und Spaltenüberschriften in den Unterplots von matplotlib


82

Was ist die beste Vorgehensweise, um einem Raster von Teilplots, die in einer Schleife in generiert wurden, eine Zeilen- und eine Spaltenüberschrift hinzuzufügen matplotlib? Ich kann mir ein Paar vorstellen, aber nicht besonders ordentlich:

  1. Für Spalten mit einem Zähler für Ihre Schleife können Sie nur set_title()für die erste Zeile verwenden. Für Zeilen funktioniert dies nicht. Sie müssten textaußerhalb der Grundstücke zeichnen .
  2. Sie fügen oben eine zusätzliche Reihe von Unterplots und links eine zusätzliche Spalte mit Unterplots hinzu und zeichnen Text in die Mitte dieses Unterplots.

Können Sie eine bessere Alternative vorschlagen?

Geben Sie hier die Bildbeschreibung ein

Antworten:


109

Es gibt verschiedene Möglichkeiten, dies zu tun. Der einfache Weg besteht darin, die y-Labels und Titel des Plots auszunutzen und dann fig.tight_layout()Platz für die Labels zu schaffen. Alternativ können Sie zusätzlichen Text an der richtigen Stelle mit platzieren annotateund dann halb manuell Platz dafür schaffen.


Wenn Ihre Achsen keine Y-Beschriftungen haben, können Sie den Titel und die Y-Beschriftung der ersten Zeile und Spalte der Achsen leicht ausnutzen.

import matplotlib.pyplot as plt

cols = ['Column {}'.format(col) for col in range(1, 4)]
rows = ['Row {}'.format(row) for row in ['A', 'B', 'C', 'D']]

fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(12, 8))

for ax, col in zip(axes[0], cols):
    ax.set_title(col)

for ax, row in zip(axes[:,0], rows):
    ax.set_ylabel(row, rotation=0, size='large')

fig.tight_layout()
plt.show()

Geben Sie hier die Bildbeschreibung ein


Wenn Sie über Y-Labels verfügen oder etwas mehr Flexibilität bevorzugen, können Sie annotatedie Labels platzieren. Dies ist komplizierter, ermöglicht es Ihnen jedoch, zusätzlich zu den Zeilen- und Spaltenbeschriftungen einzelne Plottitel, ylabels usw. zu verwenden.

import matplotlib.pyplot as plt
from matplotlib.transforms import offset_copy


cols = ['Column {}'.format(col) for col in range(1, 4)]
rows = ['Row {}'.format(row) for row in ['A', 'B', 'C', 'D']]

fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(12, 8))
plt.setp(axes.flat, xlabel='X-label', ylabel='Y-label')

pad = 5 # in points

for ax, col in zip(axes[0], cols):
    ax.annotate(col, xy=(0.5, 1), xytext=(0, pad),
                xycoords='axes fraction', textcoords='offset points',
                size='large', ha='center', va='baseline')

for ax, row in zip(axes[:,0], rows):
    ax.annotate(row, xy=(0, 0.5), xytext=(-ax.yaxis.labelpad - pad, 0),
                xycoords=ax.yaxis.label, textcoords='offset points',
                size='large', ha='right', va='center')

fig.tight_layout()
# tight_layout doesn't take these labels into account. We'll need 
# to make some room. These numbers are are manually tweaked. 
# You could automatically calculate them, but it's a pain.
fig.subplots_adjust(left=0.15, top=0.95)

plt.show()

Geben Sie hier die Bildbeschreibung ein


8
Die Methoden is_first_col(), is_last_col(), is_first_row()und is_last_row()können in diesem Zusammenhang auch bequem sein.
Gerrit

1
Als Anmerkung hat annotate matplotlib die Option drehen. Wenn Sie also Ihre Beschriftung um 90 Grad drehen möchten, fügen Sie einfach das Argument hinzurotation = 90
mathishard.butweloveit

2

Die obige Antwort funktioniert. Nur nicht, dass Sie in der zweiten Version der Antwort:

for ax, row in zip(axes[:,0], rows):
    ax.annotate(col, xy=(0, 0.5), xytext=(-ax.yaxis.labelpad-pad,0),
                xycoords=ax.yaxis.label, textcoords='offset points',
                size='large', ha='right', va='center')

anstatt:

for ax, row in zip(axes[:,0], rows):
    ax.annotate(row,xy=(0, 0.5), xytext=(-ax.yaxis.labelpad-pad,0),                    
                xycoords=ax.yaxis.label, textcoords='offset points',
                size='large', ha='right', va='center')
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.