Problem with simple use of draw() in animations of arrays

Hi,

I have a problem with draw() to do simple animations of the contents of arrays in matplotlib.

I was trying to use the idea in the animations cookbook (http://www.scipy.org/Cookbook/Matplotlib/Animations)

to animate some “random walkers”, but found that the animation did not work.

A minimal example of the problem is given by changing the first recipe in the cookbook to just draw random arrays.
This version correctly animates as expected:

from pylab import *
import time

ion()

tstart = time.time() # for profiling
x = rand(100)
y = rand(100)
line, = plot(x,y, ‘,’)
for i in arange(1,200):
x = rand(100)

y = rand(100)
line.set_data(x,y)
draw()                         # redraw the canvas

Now, however, changing the “x =” and “y =” lines as follows:
x[:] = rand(100)
y[:] = rand(100)

so that they are modified in place, rather than creating new arrays, no longer animates anything.
I am using version 0.99 on linux (Kubuntu 9.10).
The same behaviour is found from within ipython -pylab or from the command line with python.

In my real application, I wish to use this as a simple way to animate a collection of random walkers.
I thus have an array of positions which is updated at every step, and this is what I want to animate,
which is the reason why I tried the array updating step above.

So far, my code is as follows:

from pylab import *

ion()

N = 1000
pos = zeros((N,2))

figure(figsize=(8,8))
points, = plot(pos[:,0], pos[:,1], ‘,’)
axis([-20,20,-20,20])

for t in range(1000):

pos += uniform(-1,1,N*2).reshape(N,2)
points.set_data(pos[:,0].copy(), pos[:,1].copy())
draw()

The “.copy()” are an attempt at creating new arrays. Nonetheless, there is no animation.

And if I put
points._x
in ipython, then it still has all zeros! Apparently the .set_data() hasn’t done anything.

Any help at getting this animation to work are greatly appreciated!

Thanks and best wishes,

David.

The Line2D object keeps the input data as a cache and only update it
(recache) if the new data is different than the cached one.
The problem in this particular case is that the cache is actually a
*pos* itself. And modifying the pos in place, actually modify the
cache in the Line2D object. Thus, set_data sees that the given data is
identical to the cached one, and skip the recaching.
I'm not sure what is the best approach here, and I defer the fix (or
not) to others.
Meanwhile, you can force the recaching with recache method. i.e., call
points.recache() after set_data. You don't need to make a copy also.
As a matter of fact, I think it will give you a best performance (but
not tested) if you directly update the cached data and do not call
set_data.
Note that in this particular case, pos == cache, so you actually don't
need to call get_data, but this is not a general case.

posx, posy = points.get_data(orig=True)

for t in range(100):
    dx, dy = uniform(-1,1,N*2).reshape(2, N) # note the change in the shape
    posx += dx
    posy += dy
    points.recache()
    draw()

Regards,

-JJ

···

On Mon, Nov 2, 2009 at 10:52 PM, David Sanders <dpsanders@...287...> wrote:

from pylab import *

ion()

N = 1000
pos = zeros((N,2))

figure(figsize=(8,8))
points, = plot(pos[:,0], pos[:,1], ',')
axis([-20,20,-20,20])

for t in range(1000):

pos \+= uniform\(\-1,1,N\*2\)\.reshape\(N,2\)
points\.set\_data\(pos\[:,0\]\.copy\(\), pos\[:,1\]\.copy\(\)\)
draw\(\)

Dear JJ,

Many thanks for your answer – “points.recache()” is exactly what I was looking for to make my animations work.

It seems to me that this must be a reasonably common question, but I could not find it in the documentation.

Perhaps it could be added to the animation cookbook?

Thanks and best wishes,
David.

PS: Apologies for the late reply – I was travelling with difficult internet access.

···

On Mon, Nov 2, 2009 at 11:51 PM, Jae-Joon Lee <lee.j.joon@…287…> wrote:

On Mon, Nov 2, 2009 at 10:52 PM, David Sanders <dpsanders@…287…> wrote:

from pylab import *

ion()

N = 1000

pos = zeros((N,2))

figure(figsize=(8,8))

points, = plot(pos[:,0], pos[:,1], ‘,’)

axis([-20,20,-20,20])

for t in range(1000):

pos += uniform(-1,1,N*2).reshape(N,2)
points.set_data(pos[:,0].copy(), pos[:,1].copy())
draw()

The Line2D object keeps the input data as a cache and only update it

(recache) if the new data is different than the cached one.

The problem in this particular case is that the cache is actually a

pos itself. And modifying the pos in place, actually modify the

cache in the Line2D object. Thus, set_data sees that the given data is

identical to the cached one, and skip the recaching.

I’m not sure what is the best approach here, and I defer the fix (or

not) to others.

Meanwhile, you can force the recaching with recache method. i.e., call

points.recache() after set_data. You don’t need to make a copy also.

As a matter of fact, I think it will give you a best performance (but

not tested) if you directly update the cached data and do not call

set_data.

Note that in this particular case, pos == cache, so you actually don’t

need to call get_data, but this is not a general case.

posx, posy = points.get_data(orig=True)

for t in range(100):

dx, dy = uniform(-1,1,N*2).reshape(2, N) # note the change in the shape

posx += dx

posy += dy

points.recache()

draw()