1

I have 10 images of the digits 0-9, each with 28x28 pixels contained in an array X of shape (28**2, 10).

I am updating X with new pixels within a loop and I would like to update my plot at each iteration.

Currently, my code will create 100 separate figures.

def plot_output(X):
    """grayscale images of the digits 0-9
    in 28x28 pixels in pyplot

    Input, X is of shape (28^2, 10)
    """
    n = X.shape[1] # number of digits
    pixels = (28,28) # pixel shape
    fig, ax = plt.subplots(1,n)

    # cycle through digits from 0-9
    # X input array is reshaped for each 10 digits
    # to a (28,28) vector to plot
    for i in range(n):
        wi=X[:,i] # w = weights for digit

        wi=wi.reshape(*pixels)
        ax[i].imshow(wi,cmap=plt.cm.gist_gray,
            interpolation='gaussian', aspect='equal')
        ax[i].axis('off')
        ax[i].set_title('{0:0d}'.format(i))

    plt.tick_params(axis='x', which='both', bottom='off',
        top='off', labelbottom='off')

    plt.show()

for i in range(100):
    X = init_pix() # anything that generates a (728, 10) array
    plot_output(X)

I have tried using plt.draw() and pt.canvas.draw() but I can't seem to implement it correctly. I have also tried plt.clf() which didn't work for me either.

I was able to do this fine using lines and one axis using this post but I can't get it to work on subplots.

Community
  • 1
  • 1
Alexander McFarlane
  • 10,643
  • 9
  • 59
  • 100

2 Answers2

2

By using plt.ion() you can make the plt.show() command, that is usually blocking, not block.

Then you can update the axes with imshow, and they'll appear in your figure as they're computed.

For example:

import numpy as np
import matplotlib.pyplot as plt

n=10

X = np.random.rand(28**2,n)

fig, ax = plt.subplots(1,n)

plt.ion()
plt.show()

for i in range(n):
    wi = X[:,1].reshape(28,28)
    ax[i].imshow(wi)

    #fig.canvas.draw()  # May be necessary, wasn't for me.

plt.ioff()  # Make sure to make plt.show() blocking again, otherwise it'll run
plt.show()  #   right through this and immediately close the window (if program exits)

You'll now get ugly huge empty white axes until your axes are defined, but this should get you started.

jedwards
  • 29,432
  • 3
  • 65
  • 92
  • thanks! This works nicely, I took this answer with `plt.ion()` and incorporated it into my answer. I didn't find that I had to switch interactive mode on and off though – Alexander McFarlane Apr 20 '15 at 01:15
  • 1
    @alexmcf sorry for being unclear, [`plt.ion()`](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.ion) is actually what I meant by turning on on interactive mode. `ion` presumably stands for **I**nteractive mode **ON** (note the bolding). If you don't have to turn if off after the loop, that's interesting, because in my testing, if I didn't turn it back off, the final `plt.show()` would not block, the program would proceed, and when it ended, the plot was automatically closed. – jedwards Apr 20 '15 at 05:49
  • Thanks, this was the hint I needed :) for me `fig.canvas.draw()` was necessary. – ppasler Jun 07 '16 at 15:30
0

I found a solution to this by creating a plot class and using .cla() on each axis and then re-defining each with imshow()

class plot_output(object):

    def __init__(self, X):
        """grayscale images of the digits 1-9
        """
        self.X = X
        self.n = X.shape[1] # number of digits
        self.pixels = (25,25) # pixel shape
        self.fig, self.ax = plt.subplots(1,self.n)
        plt.ion()

        # cycle through digits from 0-9
        # X input vector is reshaped for each 10 digits
        # to a (28,28) vector to plot
        self.img_obj_ar = []

        for i in range(self.n):
            wi=X[:,i] # w = weights for digit

            wi=wi.reshape(*self.pixels)
            self.ax[i].imshow(wi,cmap=plt.cm.gist_gray,
                interpolation='gaussian', aspect='equal')
            self.ax[i].axis('off')
            self.ax[i].set_title('{0:0d}'.format(i))

        plt.tick_params(\
            axis='x',          # changes apply to the x-axis
            which='both',      # both major and minor ticks are affected
            bottom='off',      # ticks along the bottom edge are off
            top='off',         # ticks along the top edge are off
            labelbottom='off')

        plt.tick_params(\
            axis='y',          # changes apply to the y-axis
            which='both',      # both major and minor ticks are affected
            left='off', 
            right='off',    # ticks along the top edge are off
            labelleft='off')

        plt.show()

    def update(self, X):

        # cycle through digits from 0-9
        # X input vector is reshaped for each 10 digits
        # to a (28,28) vector to plot
        for i in range(self.n):
            self.ax[i].cla()
            wi=X[:,i] # w = weights for digit

            wi=wi.reshape(*self.pixels)
            self.ax[i].imshow(wi,cmap=plt.cm.gist_gray,
                            interpolation='gaussian', aspect='equal')
            self.ax[i].axis('off')
            self.ax[i].set_title('{0:0d}'.format(i))

        plt.draw()
Alexander McFarlane
  • 10,643
  • 9
  • 59
  • 100