113 votes

Puis-je créer des objets AxesSubplot, puis les ajouter à une instance de Figure ?

En regardant la matplotlib il semble que la façon standard d'ajouter un élément AxesSubplot à un Figure est d'utiliser Figure.add_subplot :

from matplotlib import pyplot

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

J'aimerais pouvoir créer AxesSubPlot -Je peux donc les utiliser dans différentes figures. Quelque chose comme

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)

Est-ce possible en matplotlib et si oui, comment puis-je le faire ?

Mise à jour : Je n'ai pas réussi à découpler la création des axes et des figures, mais en suivant les exemples des réponses ci-dessous, je peux facilement réutiliser des axes précédemment créés dans des instances de figures nouvelles ou anciennes. Ceci peut être illustré par une simple fonction :

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

57voto

Joe Kington Points 68089

En règle générale, il suffit de transmettre l'instance des axes à une fonction.

Par exemple :

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()

Pour répondre à votre question, vous pouvez toujours faire quelque chose comme ça :

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

Vous pouvez également ajouter une instance d'axe à une autre figure :

import matplotlib.pyplot as plt

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

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

plt.show()

Il est également possible de la redimensionner pour qu'elle corresponde à d'autres "formes" d'intrigues secondaires, mais cela risque de poser rapidement plus de problèmes que cela n'en vaut la peine. D'après mon expérience, l'approche qui consiste à passer une instance de figure ou d'axe (ou une liste d'instances) est beaucoup plus simple pour les cas complexes...

39voto

Ce qui suit montre comment "déplacer" un axe d'une figure à l'autre. C'est la fonctionnalité prévue de Le dernier exemple de @JoeKington qui, dans les versions plus récentes de matplotlib, ne fonctionne plus, car les axes ne peuvent pas vivre dans plusieurs figures à la fois.

Il faudrait d'abord supprimer les axes de la première figure, puis l'ajouter à la figure suivante et lui donner une position dans laquelle vivre.

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()

4voto

Steve Tjoa Points 15116

Pour les tracés linéaires, vous pouvez utiliser la fonction Line2D les objets eux-mêmes :

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])

1voto

Xopi García Points 111

TL;DR basée en partie sur Joe belle réponse.

Opt.1 : fig.add_subplot()

def fcn_return_plot():
    return plt.plot(np.random.random((10,)))
n = 4
fig = plt.figure(figsize=(n*3,2))
#fig, ax = plt.subplots(1, n,  sharey=True, figsize=(n*3,2)) # also works
for index in list(range(n)):
    fig.add_subplot(1, n, index + 1)
    fcn_return_plot()
    plt.title(f"plot: {index}", fontsize=20) 

Opt.2 : passe ax[index] à une fonction qui renvoie ax[index].plot()

def fcn_return_plot_input_ax(ax=None):
    if ax is None:
        ax = plt.gca()
    return ax.plot(np.random.random((10,)))
n = 4
fig, ax = plt.subplots(1, n,  sharey=True, figsize=(n*3,2))
for index in list(range(n)):
    fcn_return_plot_input_ax(ax[index])
    ax[index].set_title(f"plot: {index}", fontsize=20)

Respect des sorties. enter image description here enter image description here

Note : Opt.1 plt.title() modifié dans l'opt.2 en ax[index].set_title() . En savoir plus Matplotlib Gotchas dans le livre de Van der Plas .

0voto

Xopi García Points 111

Pour aller plus loin dans le trou du lapin. En prolongeant ma réponse précédente, on pourrait renvoyer tout un ax et non ax.plot() seulement. Par exemple

Si la base de données contient 100 tests de 20 types (ici id) :

dfA = pd.DataFrame(np.random.random((100,3)), columns = ['y1', 'y2', 'y3'])
dfB = pd.DataFrame(np.repeat(list(range(20)),5), columns = ['id'])
dfC = dfA.join(dfB)

Et la fonction de tracé (c'est la clé de toute cette réponse) :

def plot_feature_each_id(df, feature, id_range=[], ax=None, legend_bool=False):
    feature = df[feature]
    if not len(id_range): id_range=set(df['id'])
    legend_arr = []
    for k in id_range:
        pass
        mask = (df['id'] == k)
        ax.plot(feature[mask])
        legend_arr.append(f"id: {k}")
    if legend_bool: ax.legend(legend_arr)
    return ax

Nous pouvons atteindre nos objectifs :

feature_arr = dfC.drop('id',1).columns
id_range= np.random.randint(len(set(dfC.id)), size=(10,))
n = len(feature_arr)
fig, ax = plt.subplots(1, n,  figsize=(n*6,4));
for i,k in enumerate(feature_arr):
    plot_feature_each_id(dfC, k, np.sort(id_range), ax[i], legend_bool=(i+1==n))
    ax[i].set_title(k, fontsize=20)
    ax[i].set_xlabel("test nr. (id)", fontsize=20)

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X