matplotlib: Kann ich AxesSubplot-Objekte erstellen und sie dann einer Figure-Instanz hinzufügen?

76

Mit Blick auf der matplotlibDokumentation, so scheint es den normalen Weg ein hinzuzufügen , AxesSubplotum einen Figureverwenden Figure.add_subplot:

from matplotlib import pyplot

fig = pyplot.figure()
ax = fig.add_subplot(1,1,1)
ax.hist( some params .... )

Ich möchte in der Lage sein, AxesSubPlotunabhängig von der Figur ähnliche Objekte zu erstellen , damit ich sie in verschiedenen Figuren verwenden kann. Etwas wie

fig = pyplot.figure()
histoA = some_axes_subplot_maker.hist( some params ..... )
histoA = some_axes_subplot_maker.hist( some other params ..... )
# make one figure with both plots
fig.add_subaxes(histo1, 211)
fig.add_subaxes(histo1, 212)
fig2 = pyplot.figure()
# make a figure with the first plot only
fig2.add_subaxes(histo1, 111)

Ist dies möglich matplotlibund wenn ja, wie kann ich das tun?

Update: Ich habe es nicht geschafft, die Erstellung von Achsen und Figuren zu entkoppeln, aber die folgenden Beispiele in den Antworten unten können zuvor erstellte Achsen problemlos in neuen oder olf-Figuren-Instanzen wiederverwenden. Dies kann mit einer einfachen Funktion veranschaulicht werden:

def plot_axes(ax, fig=None, geometry=(1,1,1)):
    if fig is None:
        fig = plt.figure()
    if ax.get_geometry() != geometry :
        ax.change_geometry(*geometry)
    ax = fig.axes.append(ax)
    return fig
Juanchopanza
quelle
Ihre Funktion plot_axes scheint nicht mehr zu funktionieren.
Jim

Antworten:

35

In der Regel übergeben Sie die Achseninstanz einfach an eine Funktion.

Zum Beispiel:

import matplotlib.pyplot as plt
import numpy as np

def main():
    x = np.linspace(0, 6 * np.pi, 100)

    fig1, (ax1, ax2) = plt.subplots(nrows=2)
    plot(x, np.sin(x), ax1)
    plot(x, np.random.random(100), ax2)

    fig2 = plt.figure()
    plot(x, np.cos(x))

    plt.show()

def plot(x, y, ax=None):
    if ax is None:
        ax = plt.gca()
    line, = ax.plot(x, y, 'go')
    ax.set_ylabel('Yabba dabba do!')
    return line

if __name__ == '__main__':
    main()

Um auf Ihre Frage zu antworten, können Sie jederzeit Folgendes tun:

def subplot(data, fig=None, index=111):
    if fig is None:
        fig = plt.figure()
    ax = fig.add_subplot(index)
    ax.plot(data)

Sie können einer anderen Figur auch einfach eine Achseninstanz hinzufügen:

import matplotlib.pyplot as plt

fig1, ax = plt.subplots()
ax.plot(range(10))

fig2 = plt.figure()
fig2.axes.append(ax)

plt.show()

Es ist auch möglich, die Größe an andere "Formen" der Nebenhandlung anzupassen, aber es wird schnell mehr Probleme geben, als es wert ist. Der Ansatz, nur eine Figuren- oder Achseninstanz (oder eine Liste von Instanzen) herumzugeben, ist meiner Erfahrung nach für komplexe Fälle viel einfacher ...

Joe Kington
quelle
+1 Das ist nützlich, aber es scheint mir, dass die Achsen immer noch an die Figuren und / oder an einen Zustand im Pyplot gekoppelt sind. Ich kann die Achsenerstellung nicht wirklich von der Figurenerstellung und dem Plotten nach Ihrem Beispiel entkoppeln.
Juanchopanza
Achsen sind grundsätzlich mit einer bestimmten Figur in matplotlib verknüpft. Daran führt kein Weg vorbei. Sie können die Achsenerstellung jedoch immer noch vollständig vom Erstellen und Zeichnen von Figuren entkoppeln, indem Sie nur Achsen und Figurenobjekte herumführen. Ich bin nicht ganz sicher, ob ich folge, was Sie tun wollen ...
Joe Kington
Nun, ich denke, sie sind nicht so grundlegend miteinander verbunden, wie ich dachte. Sie können einfach die gleichen Achsen zu einer anderen Figur hinzufügen. (Einfach tun fig2.axes.append(ax1)) Es ist auch möglich, die Größe an verschiedene Nebenplotformen anzupassen. Dies wird wahrscheinlich mehr Ärger als es wert ist ...
Joe Kington
4
Das Hinzufügen einer Achseninstanz zu einer anderen Figur (letztes Beispiel) funktioniert in Enthought 7.3-2 (matplotlib 1.1.0) bei mir nicht.
Aaren
13
@aaren - Es funktioniert nicht, weil die Funktionsweise des Achsenstapels für eine Figur in neueren Versionen von matplotlib geändert wurde. Achsen sollten jetzt bewusst nicht mehr zwischen verschiedenen Figuren geteilt werden. fig2._axstack.add(fig2._make_key(a), a)Um dieses Problem zu umgehen , könnten Sie dies tun , aber es ist hackisch und wird sich wahrscheinlich in Zukunft ändern. Es scheint richtig zu funktionieren, aber es kann einige Dinge kaputt machen.
Joe Kington
19

Das Folgende zeigt, wie man eine Achse von einer Figur zur anderen "bewegt". Dies ist die beabsichtigte Funktionalität von @ JoeKingtons letztem Beispiel , das in neueren Matplotlib-Versionen nicht mehr funktioniert, da Achsen nicht in mehreren Figuren gleichzeitig leben können.

Sie müssten zuerst die Achsen aus der ersten Figur entfernen, sie dann an die nächste Figur anhängen und ihr eine Position zum Leben geben.

import matplotlib.pyplot as plt

fig1, ax = plt.subplots()
ax.plot(range(10))
ax.remove()

fig2 = plt.figure()
ax.figure=fig2
fig2.axes.append(ax)
fig2.add_axes(ax)

dummy = fig2.add_subplot(111)
ax.set_position(dummy.get_position())
dummy.remove()
plt.close(fig1)

plt.show()
Wichtigkeit von BeErnest
quelle
1
kleiner Zusatz: Wenn plt.show()durch fig2.savefig('out.png', dpi=300)die Positionierung ersetzt wird, wird durch das dpiSchlüsselwort durcheinander gebracht . Dies kann vermieden werden, indem das Finale festgelegt wird, dpiwenn axes initialisiert wird:fig1, ax = plt.subplots(dpi=300)
Matthias123
In meiner Python-Shell sieht es nicht so aus, als ob diese Zeile irgendetwas tut: fig2.axes.append (ax)
Spirko
ax.remove()Ergebnisse in NotImplementedError: cannot remove artist(Python 3.7.0, Matplotlib 2.2.2)
Gerrit
@gerrit Hast du etwas im Vergleich zum obigen Snippet geändert? Es funktioniert gut für mich.
ImportanceOfBeingErnest
1
@irene Beachten Sie, dass diese Lösung nur die Achsen auf eine neue Figur verschiebt und keine der Transformationen festlegt. Solche Probleme werden also erwartet. Da es nicht ratsam ist, Künstler zwischen Figuren zu bewegen, sollten Sie dies besser nicht verwenden, wenn Sie eine zuverlässige Ausgabe benötigen.
ImportanceOfBeingErnest
4

Bei Liniendiagrammen können Sie sich mit den Line2DObjekten selbst befassen :

fig1 = pylab.figure()
ax1 = fig1.add_subplot(111)
lines = ax1.plot(scipy.randn(10))

fig2 = pylab.figure()
ax2 = fig2.add_subplot(111)
ax2.add_line(lines[0])
Steve Tjoa
quelle
+1 Gutes Beispiel. Es scheint, dass ich die Achsenerstellung nicht von der Figurenerstellung entkoppeln kann, aber ich kann die Achseninstanz greifen und an neue Figuren übergeben.
Juanchopanza
1
Bitte beachten Sie, dass dieser Ansatz nicht mehr funktioniert. Siehe den Kommentar von Joe Kington vom 7. Dezember 2012 zu seiner Antwort oben.
Joelostblom
2
ax2.add_line(lines[0])Ergebnisse in RuntimeError: Can not put single artist in more than one figure(Python 3.7.0, matplotlib 2.2.2).
Gerrit