Matplotlib Application Interfaces (APIs)#

Matplotlib hat zwei Haupt-Anwendungsschnittstellen, oder Stile der Nutzung der Bibliothek

  • Eine explizite "Axes"-Schnittstelle, die Methoden auf einem Figure- oder Axes-Objekt verwendet, um andere Künstler zu erstellen und eine Visualisierung Schritt für Schritt aufzubauen. Diese wurde auch als "objektorientierte" Schnittstelle bezeichnet.

  • Eine implizite "pyplot"-Schnittstelle, die die letzte erstellte Figure und Axes im Auge behält und Künstler zu dem Objekt hinzufügt, von dem sie glaubt, dass der Benutzer es möchte.

Darüber hinaus bieten eine Reihe von nachgelagerten Bibliotheken (wie pandas und xarray) eine plot Methode, die direkt auf ihren Datenklassen implementiert ist, sodass Benutzer data.plot() aufrufen können.

Der Unterschied zwischen diesen Schnittstellen kann etwas verwirrend sein, insbesondere angesichts von Snippets im Web, die die eine oder andere oder manchmal mehrere Schnittstellen im selben Beispiel verwenden. Hier versuchen wir aufzuzeigen, wie sich die "pyplot"- und nachgelagerten Schnittstellen zur expliziten "Axes"-Schnittstelle verhalten, um Benutzern die Navigation in der Bibliothek zu erleichtern.

Native Matplotlib-Schnittstellen#

Die explizite "Axes"-Schnittstelle#

Die "Axes"-Schnittstelle ist die Art und Weise, wie Matplotlib implementiert ist, und viele Anpassungen und Feinabstimmungen werden auf dieser Ebene vorgenommen.

Diese Schnittstelle funktioniert, indem eine Instanz einer Figure-Klasse instanziiert wird (unten fig), wobei eine subplots-Methode (oder eine ähnliche) auf diesem Objekt verwendet wird, um ein oder mehrere Axes-Objekte zu erstellen (unten ax), und dann Zeichenmethoden auf den Axes aufzurufen (plot in diesem Beispiel).

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.subplots()
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

(Quellcode, 2x.png, png)

Wir nennen dies eine "explizite" Schnittstelle, da jedes Objekt explizit referenziert und zur Erstellung des nächsten Objekts verwendet wird. Das Beibehalten von Referenzen auf die Objekte ist sehr flexibel und ermöglicht es uns, die Objekte anzupassen, nachdem sie erstellt wurden, aber bevor sie angezeigt werden.

Die implizite "pyplot"-Schnittstelle#

Das pyplot-Modul überschattet die meisten Zeichenmethoden von Axes, um das Äquivalent des obigen zu bieten, wobei die Erstellung der Figure und Axes für den Benutzer erfolgt.

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

(Quellcode, 2x.png, png)

Dies kann praktisch sein, insbesondere bei interaktiver Arbeit oder einfachen Skripten. Eine Referenz auf die aktuelle Figure kann mit gcf und auf die aktuelle Axes mit gca abgerufen werden. Das pyplot-Modul verwaltet eine Liste von Figures, und jede Figure verwaltet eine Liste von Axes auf der Figure für den Benutzer, sodass das Folgende

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
plt.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
plt.plot([3, 2, 1], [0, 0.5, 0.2])

(Quellcode, 2x.png, png)

ist äquivalent zu

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
ax = plt.gca()
ax.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
ax = plt.gca()
ax.plot([3, 2, 1], [0, 0.5, 0.2])

(Quellcode, 2x.png, png)

In der expliziten Schnittstelle wäre dies

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2)
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])

(Quellcode, 2x.png, png)

Übersetzen zwischen der Axes-Schnittstelle und der pyplot-Schnittstelle#

Sie können in bestehendem Code auf eine der beiden Schnittstellen stoßen, und leider manchmal sogar auf Mischformen. Dieser Abschnitt beschreibt die Muster für spezifische Operationen in beiden Schnittstellen und wie man von einer zur anderen übersetzt.

  • Das Erstellen von Figures ist für beide Schnittstellen gleich: Verwenden Sie die entsprechenden pyplot-Funktionen plt.figure(), plt.subplots(), plt.subplot_mosaic(). Für die Axes-Schnittstelle speichern Sie typischerweise die erstellte Figure (und möglicherweise Axes) in Variablen zur späteren Verwendung. Bei Verwendung der pyplot-Schnittstelle werden diese Werte normalerweise nicht gespeichert. Beispiel

    • Axes: fig, ax = plt.subplots()

    • pyplot: plt.subplots()

  • "Plotting"-Funktionen, d. h. Funktionen, die Daten hinzufügen, haben auf der Axes und in pyplot denselben Namen und identische Parameter. Beispiel

    • Axes: ax.plot(x, y)

    • pyplot: plt.plot(x, y)

  • Funktionen, die Eigenschaften abrufen, heißen wie die Eigenschaft in pyplot und sind auf der Axes mit get_ präfigiert. Beispiel

    • Axes: label = ax.get_xlabel()

    • pyplot: label = plt.xlabel()

  • Funktionen, die Eigenschaften setzen, heißen wie die Eigenschaft in pyplot und sind auf der Axes mit set_ präfigiert. Beispiel

    • Axes: ax.set_xlabel("time")

    • pyplot: plt.xlabel("time")

Hier ist eine kurze Zusammenfassung der Beispiele noch einmal im direkten Vergleich

Operation

Axes-Schnittstelle

pyplot-Schnittstelle

Figuren erstellen

fig, ax = plt.subplots()

plt.subplots()

Daten plotten

ax.plot(x, y)

plt.plot(x, y)

Eigenschaften abrufen

label = ax.get_xlabel()

label = plt.xlabel()

Eigenschaften setzen

ax.set_xlabel("time")

plt.xlabel("time")

Warum explizit sein?#

Was passiert, wenn Sie zurückgehen und eine alte Axes bearbeiten müssen, die nicht von plt.gca() referenziert wird? Ein einfacher Weg ist, subplot erneut mit denselben Argumenten aufzurufen. Dies wird jedoch schnell unschön. Sie können auch das Figure-Objekt inspizieren und dessen Liste von Axes-Objekten abrufen, was jedoch irreführend sein kann (Colorbars sind auch Axes!). Die beste Lösung ist wahrscheinlich, ein Handle für jede von Ihnen erstellte Axes zu speichern, aber wenn Sie das tun, warum nicht einfach alle Axes-Objekte zu Beginn erstellen?

Der erste Ansatz ist, plt.subplot erneut aufzurufen

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
plt.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
plt.plot([3, 2, 1], [0, 0.5, 0.2])

plt.suptitle('Implicit Interface: re-call subplot')

for i in range(1, 3):
    plt.subplot(1, 2, i)
    plt.xlabel('Boo')

(Quellcode, 2x.png, png)

Der zweite ist, ein Handle zu speichern

import matplotlib.pyplot as plt

axs = []
ax = plt.subplot(1, 2, 1)
axs += [ax]
plt.plot([1, 2, 3], [0, 0.5, 0.2])

ax = plt.subplot(1, 2, 2)
axs += [ax]
plt.plot([3, 2, 1], [0, 0.5, 0.2])

plt.suptitle('Implicit Interface: save handles')

for i in range(2):
    plt.sca(axs[i])
    plt.xlabel('Boo')

(Quellcode, 2x.png, png)

Der empfohlene Weg wäre jedoch, von Anfang an explizit zu sein

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2)
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])
fig.suptitle('Explicit Interface')
for i in range(2):
    axs[i].set_xlabel('Boo')

(Quellcode, 2x.png, png)

Datenobjekt-Schnittstellen von Drittanbieterbibliotheken#

Einige Drittanbieterbibliotheken haben sich entschieden, das Plotten für ihre Datenobjekte zu implementieren, z. B. data.plot(), wie es in pandas, xarray und anderen Drittanbieterbibliotheken zu sehen ist. Zu Illustrationszwecken kann eine nachgelagerte Bibliothek einen einfachen Datencontainer implementieren, der x und y Daten zusammen gespeichert hat und dann eine plot Methode implementiert.

import matplotlib.pyplot as plt

# supplied by downstream library:
class DataContainer:

    def __init__(self, x, y):
        """
        Proper docstring here!
        """
        self._x = x
        self._y = y

    def plot(self, ax=None, **kwargs):
        if ax is None:
            ax = plt.gca()
        ax.plot(self._x, self._y, **kwargs)
        ax.set_title('Plotted from DataClass!')
        return ax


# what the user usually calls:
data = DataContainer([0, 1, 2, 3], [0, 0.2, 0.5, 0.3])
data.plot()

(Quellcode, 2x.png, png)

So kann die Bibliothek dem Benutzer alle Details verbergen und eine Visualisierung erstellen, die für den Datentyp geeignet ist, oft mit guten Beschriftungen, Farbauswahl und anderen praktischen Funktionen.

Im obigen Beispiel gefiel uns der vom System bereitgestellte Titel jedoch möglicherweise nicht. Glücklicherweise geben sie uns die Axes von der plot() Methode zurück, und wenn wir die explizite Axes-Schnittstelle verstehen, könnten wir aufrufen: ax.set_title('Mein bevorzugter Titel'), um den Titel anzupassen.

Viele Bibliotheken erlauben es ihren plot Methoden auch, ein optionales *ax*-Argument zu akzeptieren. Dies ermöglicht es uns, die Visualisierung in einer Axes zu platzieren, die wir platziert und möglicherweise angepasst haben.

Zusammenfassung#

Insgesamt ist es nützlich, die explizite "Axes"-Schnittstelle zu verstehen, da sie am flexibelsten ist und den anderen Schnittstellen zugrunde liegt. Ein Benutzer kann normalerweise herausfinden, wie er zur expliziten Schnittstelle wechselt und die zugrunde liegenden Objekte bearbeitet. Während die explizite Schnittstelle bei der Einrichtung etwas umständlicher sein kann, werden komplexe Plots oft einfacher sein, als zu versuchen, die implizite "pyplot"-Schnittstelle zu verwenden.

Hinweis

Es verwirrt die Leute manchmal, dass wir pyplot für beide Schnittstellen importieren. Derzeit implementiert das pyplot-Modul die "pyplot"-Schnittstelle, bietet aber auch oberflächliche Figure- und Axes-Erstellungsmethoden und startet letztendlich die grafische Benutzeroberfläche, wenn eine verwendet wird. pyplot wird also unabhängig von der gewählten Schnittstelle immer noch benötigt.

Ähnlich nutzen die deklarativen Schnittstellen, die von Partnerbibliotheken bereitgestellt werden, die Objekte, die über die "Axes"-Schnittstelle zugänglich sind, und akzeptieren diese oft als Argumente oder geben sie von Methoden zurück. Es ist normalerweise unerlässlich, die explizite "Axes"-Schnittstelle zu verwenden, um eine Anpassung der Standardvisualisierung vorzunehmen oder um die Daten in NumPy-Arrays zu entpacken und direkt an Matplotlib zu übergeben.

Anhang: "Axes"-Schnittstelle mit Datenstrukturen#

Die meisten Axes-Methoden erlauben eine weitere Adressierungs-API, indem sie ein *data*-Objekt an die Methode übergeben und die Argumente als Strings angeben.

import matplotlib.pyplot as plt

data = {'xdat': [0, 1, 2, 3], 'ydat': [0, 0.2, 0.4, 0.1]}
fig, ax = plt.subplots(figsize=(2, 2))
ax.plot('xdat', 'ydat', data=data)

(Quellcode, 2x.png, png)

Anhang: "pylab"-Schnittstelle#

Es gibt noch eine weitere Schnittstelle, von der dringend abgeraten wird, und das ist im Grunde from matplotlib.pylab import *. Dies importiert alle Funktionen aus matplotlib.pyplot, numpy, numpy.fft, numpy.linalg und numpy.random sowie einige zusätzliche Funktionen in den globalen Namensraum.

Ein solches Muster gilt als schlechte Praxis im modernen Python, da es den globalen Namensraum unübersichtlich macht. Noch schwerwiegender ist, dass im Fall von pylab einige eingebaute Funktionen überschrieben werden (z. B. wird die eingebaute Funktion sum durch numpy.sum ersetzt), was zu unerwartetem Verhalten führen kann.