How to update a plot in matplotlib

PythonMatplotlibTkinter

Python Problem Overview


I'm having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()
    
    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)

Python Solutions


Solution 1 - Python

You essentially have two options:

  1. Do exactly what you're currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.

  2. Instead of replotting, you can just update the data of the plot objects. You'll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you're plotting can't change, and if the range of your data is changing, you'll need to manually reset the x and y axis limits.

To give an example of the second option:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    fig.canvas.draw()
    fig.canvas.flush_events()

Solution 2 - Python

You can also do like the following: This will draw a 10x1 random matrix data on the plot for 50 cycles of the for loop.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

Solution 3 - Python

This worked for me. Repeatedly calls a function updating the graph every time.

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

"fun" is a function that returns an integer. FuncAnimation will repeatedly call "update", it will do that "xmax" times.

Solution 4 - Python

This worked for me:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()

Solution 5 - Python

I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab's drawnow.

An example usage:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.

Solution 6 - Python

In case anyone comes across this article looking for what I was looking for, I found examples at

https://stackoverflow.com/questions/5127668/how-to-visualize-scalar-2d-data-with-matplotlib/10252292#10252292

and

[http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop (on web.archive.org)][1]

then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.


Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

fig = plt.figure()
ax  = fig.add_subplot(111)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

I also found a much simpler way to go about this whole process, albeit less robust:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg

Thank you for the help with everything.

[1]: http://web.archive.org/web/20130114230355/http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop "http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop"

Solution 7 - Python

All of the above might be true, however for me "online-updating" of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!

Solution 8 - Python

Based on the other answers, I wrapped the figure's update in a python decorator to separate the plot's update mechanism from the actual plot. This way, it is much easier to update any plot.

def plotlive(func):
    plt.ion()

    @functools.wraps(func)
    def new_func(*args, **kwargs):

        # Clear all axes in the current figure.
        axes = plt.gcf().get_axes()
        for axis in axes:
            axis.cla()

        # Call func to plot something
        result = func(*args, **kwargs)

        # Draw the plot
        plt.draw()
        plt.pause(0.01)

        return result

    return new_func 

Usage example

And then you can use it like any other decorator.

@plotlive
def plot_something_live(ax, x, y):
    ax.plot(x, y)
    ax.set_ylim([0, 100])

The only constraint is that you have to create the figure before the loop:

fig, ax = plt.subplots()
for i in range(100):
    x = np.arange(100)
    y = np.full([100], fill_value=i)
    plot_something_live(ax, x, y)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionthenicknameView Question on Stackoverflow
Solution 1 - PythonJoe KingtonView Answer on Stackoverflow
Solution 2 - PythonArindamView Answer on Stackoverflow
Solution 3 - PythonVictor BassoView Answer on Stackoverflow
Solution 4 - PythonJulianView Answer on Stackoverflow
Solution 5 - PythonScottView Answer on Stackoverflow
Solution 6 - PythonCavemanPhDView Answer on Stackoverflow
Solution 7 - PythonjkeyserView Answer on Stackoverflow
Solution 8 - PythonLadislav OndrisView Answer on Stackoverflow