animate histogram

This is a second plea for help.
http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg12632.html

I have a figure.Figure embedded in a FigureCanvasTkAgg.
Each iteration, new data are received,
and I want an updated histogram.

Now I can at least see a way to do this with pyplot:
I just clear my axes each iteration, call ax.hist,
and then call plot.draw(). I think (?) my problem
is finding an equivalent to plt.draw() in the object
oriented interface.

What I'd really like to do each iteration is change
only the heights of the rectangles.

*But*, any clues are welcome.

Thanks,
Alan Isaac

Hi Alan,

Alan G Isaac wrote:

This is a second plea for help.
http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg12632.html

I have a figure.Figure embedded in a FigureCanvasTkAgg.
Each iteration, new data are received,
and I want an updated histogram.

Now I can at least see a way to do this with pyplot:
I just clear my axes each iteration, call ax.hist,
and then call plot.draw(). I think (?) my problem
is finding an equivalent to plt.draw() in the object
oriented interface.

What I'd really like to do each iteration is change
only the heights of the rectangles.

I don't do fancy stuff like embedding figures in GUIs, but assuming things work the same way regardless: have you tried grabbing the object handles from hist (which are patches) and using the rectangle patch functions (http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.patches.Rectangle) e.g. set_height() to change the rectangles?

e.g. code

···

------------------------------
import pylab
x = pylab.rand(20)
h = pylab.hist(x)
h[2][0].set_height(1)
pylab.draw()
--------------------------------

h is a tuple with the last element as a list of patch objects

In [82]: h
Out[82]:
(array([3, 3, 2, 1, 3, 0, 0, 1, 3, 4]),
  array([ 0.02262869, 0.11966418, 0.21669968, 0.31373517, 0.41077066,
         0.50780615, 0.60484164, 0.70187713, 0.79891263, 0.89594812,
         0.99298361]),
  <a list of 10 Patch objects>)

Best
-Kaushik

This is the right idea, but it is likely to be slow for animation,
since each rectangle is a separate mpl object, each with its own
graphics contexs, rendering step, etc. bar and hist were very early
functions which I wrote before we had collections and compound paths.
If I were rewriting it from scratch, I would use a compound path.
It's a little more work upfront because you have to manually compute
the vertices and path codes, but it will be goo-gobs faster. Here is
the animated histogram for tk using compound paths

"""
This example shows how to use a path patch to draw a bunch of
rectangles for an animated histogram
"""
import numpy as np
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path

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

# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)

# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
nrects = len(left)

# here comes the tricky part -- we have to set up the vertex and path
# codes arrays using moveto, lineto and closepoly

# for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the
# CLOSEPOLY; the vert for the closepoly is ignored but we still need
# it to keep the codes aligned with the vertices
nverts = nrects*(1+3+1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5,0] = left
verts[0::5,1] = bottom
verts[1::5,0] = left
verts[1::5,1] = top
verts[2::5,0] = right
verts[2::5,1] = top
verts[3::5,0] = right
verts[3::5,1] = bottom

barpath = path.Path(verts, codes)
patch = patches.PathPatch(barpath, facecolor='green',
edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)

ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())

def animate():
    # simulate new data coming in
    data = np.random.randn(1000)
    n, bins = np.histogram(data, 100)
    top = bottom + n
    verts[1::5,1] = top
    verts[2::5,1] = top
    fig.canvas.draw()

def run():
    for i in range(100):
        fig.canvas.manager.window.after(100, animate)

fig.canvas.manager.window.after(100, run)
plt.show()

histogram_tkagg.py (1.85 KB)

···

On Sat, Aug 8, 2009 at 6:17 AM, Kaushik Ghose<Kaushik_Ghose@...2126...> wrote:

(http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.patches.Rectangle)
e.g. set_height() to change the rectangles?

e.g. code
------------------------------
import pylab
x = pylab.rand(20)
h = pylab.hist(x)
h[2][0].set_height(1)
pylab.draw()
--------------------------------

Oops, in my last post I have the timer logic wrong because the call is
non-blocking so the animation ran too fast -- a rare occurrence in the
world of mpl animation :slight_smile:

Here is the corrected example:

"""
This example shows how to use a path patch to draw a bunch of
rectangles for an animated histogram
"""
import time
import numpy as np
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path

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

# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)

# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
nrects = len(left)

# here comes the tricky part -- we have to set up the vertex and path
# codes arrays using moveto, lineto and closepoly

# for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the
# CLOSEPOLY; the vert for the closepoly is ignored but we still need
# it to keep the codes aligned with the vertices
nverts = nrects*(1+3+1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5,0] = left
verts[0::5,1] = bottom
verts[1::5,0] = left
verts[1::5,1] = top
verts[2::5,0] = right
verts[2::5,1] = top
verts[3::5,0] = right
verts[3::5,1] = bottom

barpath = path.Path(verts, codes)
patch = patches.PathPatch(barpath, facecolor='green',
edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)

ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())

def animate():
    if animate.cnt>=100:
        return

    animate.cnt += 1
    # simulate new data coming in
    data = np.random.randn(1000)
    n, bins = np.histogram(data, 100)
    top = bottom + n
    verts[1::5,1] = top
    verts[2::5,1] = top
    fig.canvas.draw()
    fig.canvas.manager.window.after(100, animate)
animate.cnt = 0
fig.canvas.manager.window.after(100, animate)
plt.show()

histogram_tkagg.py (1.83 KB)

···

On Sat, Aug 8, 2009 at 9:00 AM, John Hunter<jdh2358@...287...> wrote:

"""
This example shows how to use a path patch to draw a bunch of
rectangles for an animated histogram
"""

Seems perfect!
Time to study it.

Thanks!
Alan

···

On 8/8/2009 10:09 AM John Hunter apparently wrote:

Here is the corrected example:

OK, I mostly understand John's example and have
adapted it in the attached Histogram class, for
whoever might care. (The file is a working
example.) Thanks!

Here are my remaining questions.

1. To get a new histogram, I just change the
data in the vertices object and then ask my
FigureCanvasTkAgg to ``show`` itself. How
does this work? (I suppose that this FigureCanvas
has my figure, the figure references my axes, my axes
references my PathPatch, and my PathPatch references
the rectverts, and each looks to the next when I call
show?)

2. This is pretty fast. Would there be additional
speed gains to blitting, and if so, how would it
be done? (I'm just asking for clues, not a complete
example.) I expected to be able to set the animated
property on the patch when I called ax.add_patch,
but that does not work; am I supposed to just set
it directly? (I had supposed that the axes were
being informed e.g. when setting animated=True
for an ax.plot, but now I'm guessing that supposition
is wrong the `plot` just provides this as a convenience.)

If I have unveiled some radical misconceptions,
sorry, I don't have experience with GUI stuff.

Thanks,
Alan Isaac

Ooops, forgot the attachment.
Alan

histogram_tkagg.py (4.18 KB)

OK, I mostly understand John's example and have
adapted it in the attached Histogram class, for
whoever might care. (The file is a working
example.) Thanks!

Here are my remaining questions.

1. To get a new histogram, I just change the
data in the vertices object and then ask my
FigureCanvasTkAgg to ``show`` itself. How
does this work? (I suppose that this FigureCanvas
has my figure, the figure references my axes, my axes
references my PathPatch, and my PathPatch references
the rectverts, and each looks to the next when I call
show?)

if you have already created the tk gui window and shown it, just call

  fig.canvas.draw()

on each update of the vertices

2. This is pretty fast. Would there be additional
speed gains to blitting, and if so, how would it
be done? (I'm just asking for clues, not a complete
example.) I expected to be able to set the animated
property on the patch when I called ax.add_patch,
but that does not work; am I supposed to just set
it directly? (I had supposed that the axes were
being informed e.g. when setting animated=True
for an ax.plot, but now I'm guessing that supposition
is wrong the `plot` just provides this as a convenience.)

The animated property has to be on the patch itself, so when you
create the path patch, you would do

  patch = patches.PatchPath(path, animated=True)
  ax.add_patch(patch)

JDH

···

On Tue, Aug 18, 2009 at 12:53 PM, Alan G Isaac<alan.isaac@...287...> wrote:

Blitting will improve the performance when significant portion of your
plot is (semi-)stationary. And I guess your example, as it is, may not
benefit from blitting.

A. init
  1. draw stationary artists (animated=False) on the canvas
  2. save the area of canvas as a pixmap array.

B. looping for animation
  1. restore the stationary artists by blitting the saved pixmap array
  2. draw animated artists.
  3. optionally update the saved pixmap.

And your speed gain comes from B.1

Regards,

-JJ

···

On Tue, Aug 18, 2009 at 1:53 PM, Alan G Isaac<alan.isaac@...287...> wrote:

2. This is pretty fast. Would there be additional
speed gains to blitting, and if so, how would it
be done? (I'm just asking for clues, not a complete
example.)