[PYTHON] Créez un générateur d'axes et dessinez un nombre infini de graphiques

introduction

Lors de la reconnaissance d'images sur jupyter, vous souhaiterez peut-être organiser de nombreuses images et graphiques. Affichez toutes les images des enseignants MNIST. (Si tu le fais vraiment, tu ne reviendras pas et je pense que ça va tomber)

show_mnist_NG.py


import matplotlib.cm as cm
import matplotlib.pyplot as plt
import keras

mnist = keras.datasets.mnist.load_data()
(X_train, t_train), (X_test, t_test) = mnist

f, ax = plt.subplots(3000,20, figsize=(20,3000))
axes = ax.reshape((-1,))
for x, ax in zip(X_train, axes):
    ax.imshow(x, cmap=cm.gray)
    ax.axis('equal')
    ax.axis('off')
plt.show()

Cependant, il devenait ennuyeux d'appeler matplotlib chaque fois que je dessinais un graphique ou une image. Par conséquent, j'ai créé un mécanisme pour l'afficher sans autorisation.

matplotlib.pyplot.subplots()

Utilisez subplots () pour organiser les images et les graphiques.

subplots_rowcol.py


import matplotlib.pyplot as plt

nrows = 3
ncols = 5
f, axes = plt.subplots(nrows, ncols, squeeze=False, figsize=(ncols*2.0, nrows*2.0))
for i in range(13):
    r = i // ncols
    c = i % ncols
    axes[r,c].plot(range(10), range(r+c,10+r+c))  #Graphique approprié
plt.show()

Mais aux sous-graphiques (),

Il présente l'inconvénient de ne pas être facile à utiliser. Surtout, il est difficile à utiliser lorsque vous souhaitez visualiser facilement l'image sur jupyter.

Ne devrions-nous pas simplement plt.show () ligne par ligne?

La première idée que j'ai eue était d'appeler subplots () et de show () ligne par ligne.

subplots_1row.py


import matplotlib.pyplot as plt

ncols = 5
f, axes = plt.subplots(1, ncols, figsize=(ncols*2.0, 2.0))
for i in range(13):
    axes[i % 5].plot(range(10), range(i,10+i))  #Graphique approprié
    if i % 5 == 4:
        plt.show()
        f, axes = plt.subplots(1, ncols, figsize=(ncols*2.0, 2.0))
plt.show()

Lors de l'affichage d'un grand nombre d'images, elles s'affichent petit à petit, de sorte que la sensation d'attente est grandement réduite. Vous n'avez pas à penser à la mise en page car vous n'avez besoin que du nombre de graphiques pour une ligne et des tailles verticale et horizontale.

C'est bien si vous souhaitez l'intégrer dans une simple boucle. Cependant, le processus de vérification des axes est fastidieux.

Ne serait-il pas agréable d'avoir un générateur?

Je ne veux pas écrire le processus gênant plusieurs fois, alors j'en ai fait un générateur.

axes_generator.py


import matplotlib.pyplot as plt

def axes_generator(ncols):
    while True:
        f, axes = plt.subplots(1, ncols, figsize=(ncols*2.0, 2.0))
        for c in range(ncols):
            yield axes[c]
        plt.show()

ag = axes_generator(5)
for i, ax in zip(range(13), ag):
    ax.plot(range(10), range(i,10+i))  #Graphique approprié
plt.show()

Grâce au générateur, le processus de dessin du graphe peut lui être consacré. L'intérieur de la boucle est rafraîchissant et facile à lire.

Si vous voulez ajouter un graphe, appelez __next__ () et l'itérateur retournera les axes suivants.

ag = axes_generator(5)
for i, ax in zip(range(13), ag):
    ax.plot(range(10), range(i,10+i))  #Graphique approprié
    if i % 3 == 2:
        ax = ag.__next__()
        ax.bar(range(5), range(i,i+5))  #Graphique approprié
plt.show()

Mais c'est gênant de voir les haches supplémentaires.

Ne serait-il pas agréable d'avoir une classe de générateurs?

Je voulais cacher l'excès, j'ai donc décidé de le classer. --Peut être utilisé comme générateur.

AxesGenerator.py


import matplotlib.pyplot as plt

class AxesGenerator:
    def __init__(self, ncols:int=6, figsize:tuple=None, *args, **kwargs):
        self._ncols = ncols
        self._figsize = figsize
        self._axes = []

    def __iter__(self):
        while True:
            yield self.get()

    def get(self):
        if len(self._axes) == 0:
            plt.show()
            f, axes = plt.subplots(nrows=1, ncols=self._ncols, figsize=self._figsize)
            self._axes = list(axes) if self._ncols > 1 else [axes,]
        ax = self._axes.pop(0)
        return ax

    def flush(self):
        for ax in self._axes:
            ax.axis('off')
        plt.show()
        self._axes = []

ncols = 5
ag = AxesGenerator(ncols, figsize=(ncols*2.0, 2.0))
for i, ax in zip(range(13), ag):
    ax.plot(range(10), range(i,10+i))  #Graphique approprié
    if i % 3 == 2:
        ax = ag.get()
        ax.bar(range(5), range(i,i+5))  #Graphique approprié
ag.flush()

Ça devient plutôt bon. Cependant, j'appelle flush () pour nettoyer, mais j'utilise par erreur plt.show (), j'oublie de l'écrire, ou autre chose.

Si c'est un problème à nettoyer, pourquoi ne pas s'en remettre à la phrase avec?

Vous devriez pouvoir utiliser l'instruction with pour nettoyer. Pour avec, \ _ \ _ entrez \ _ \ _ () et \ _ \ _ exit \ _ \ _ () ont été ajoutés.

Au fait, le constructeur accepte maintenant l'argument de subplots ().

AxesGenerator.py


import matplotlib.pyplot as plt

class AxesGenerator:
    def __init__(self, ncols:int=6, sharey=False, subplot_kw=None, gridspec_kw=None, **fig_kw):
        self._ncols = ncols
        self._sharey = sharey
        self._subplot_kw = subplot_kw
        self._gridspec_kw = gridspec_kw
        self._fig_kw = fig_kw
        self._axes = []

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.flush()
        return True  #La gestion des exceptions est omise

    def __iter__(self):
        while True:
            yield self.get()

    def get(self):
        if len(self._axes) == 0:
            plt.show()
            f, axes = plt.subplots(nrows=1, ncols=self._ncols, sharey=self._sharey, subplot_kw=self._subplot_kw, gridspec_kw=self._gridspec_kw, **self._fig_kw)
            self._axes = list(axes) if self._ncols > 1 else [axes,]
        ax = self._axes.pop(0)
        return ax

    def flush(self):
        for ax in self._axes:
            ax.axis('off')
        plt.show()
        self._axes = []

ncols = 5
with AxesGenerator(ncols, figsize=(ncols*2.0, 2.0)) as ag:
    for i, ax in zip(range(13), ag):
        ax.plot(range(10), range(i,10+i))  #Graphique approprié
        if i % 3 == 2:
            ax = ag.get()
            ax.bar(range(5), range(i,i+5))  #Graphique approprié

en conclusion

J'ai pu atteindre l'objectif de visualiser rapidement un grand nombre de graphiques et d'images. Maintenant, vous pouvez même afficher les 60 000 images des enseignants du MNIST. (Je pense que si tu le fais vraiment, ça tombera en chemin)

show_mnist_OK.py


import matplotlib.cm as cm
import matplotlib.pyplot as plt
import keras

mnist = keras.datasets.mnist.load_data()
(X_train, t_train), (X_test, t_test) = mnist

with AxesGenerator(ncols=20, figsize=(20,1)) as ag:
    for x, ax in zip(X_train, ag):
        ax.imshow(x, cmap=cm.gray)
        ax.axis('equal')
        ax.axis('off')

J'ai l'impression que je peux rendre le code un peu plus intelligent, mais un jour.

Recommended Posts

Créez un générateur d'axes et dessinez un nombre infini de graphiques
Créer une matrice de corrélation partielle et dessiner un graphique indépendant
Dessinez une animation de flux semblable à la Terre avec matplotlib et cartopy