12

I am trying to animate a pcolormesh in matplotlib. I have seen many of the examples using the package animation, most of them using a 1D plot routine, and some of them with imshow(). First, I wan to use the FuncAnimation routine. My problem is, first, that I do not know if I can initialize the plot

fig,ax = plt.subplots()
quad = ax.pcolormesh(X,Y,Z)

I have tried a few simple lines:

fig,ax = plt.subplots()
quad = ax.pcolormesh([])

def init():
    quad.set_array([])
    return quad,

def animate(ktime):  
    quad.set_array(X,Y,np.sin(Z)+ktime)
return quad,

anim = animation.FuncAnimation(fig,animate,init_func=init,frames=Ntime,interval=200,blit=True)

plt.show()

By the way, How do I set labels into and animated plot? Can I animate the title, if it is showing a number that changes in time? Thanks

Thomas Kühn
  • 8,702
  • 3
  • 40
  • 56
AlexNoir
  • 729
  • 3
  • 8
  • 20

4 Answers4

16

The problem was that I was wrongly using set_array() routine. It is very important to note that you must pass a 1D array to this routine. To do so, regarding that color, pcolormesh and so on usually plots multidimensional arrays, you should use .ravel() . One more important thing: In order to animate different plots at the same time, the blitz option at animate.FuncAnimation must be False (See section "Animating selected plot elements" of this link).

Here I post the code that simple program with various subplots:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.animation as animation

y, x = np.meshgrid(np.linspace(-10, 10,100), np.linspace(-10, 10,100))

z = np.sin(x)*np.sin(x)+np.sin(y)*np.sin(y)

v = np.linspace(-10, 10,100)
t = np.sin(v)*np.sin(v)
tt = np.cos(v)*np.cos(v)
###########

fig = plt.figure(figsize=(16, 8),facecolor='white')
gs = gridspec.GridSpec(5, 2)
ax1 = plt.subplot(gs[0,0])

line, = ax1.plot([],[],'b-.',linewidth=2)
ax1.set_xlim(-10,10)
ax1.set_ylim(0,1)
ax1.set_xlabel('time')
ax1.set_ylabel('amplitude')
ax1.set_title('Oscillationsssss')
time_text = ax1.text(0.02, 0.95, '', transform=ax1.transAxes)

#############################
ax2 = plt.subplot(gs[1:3,0])
quad1 = ax2.pcolormesh(x,y,z,shading='gouraud')
ax2.set_xlabel('time')
ax2.set_ylabel('amplitude')
cb2 = fig.colorbar(quad1,ax=ax2)

#########################
ax3 = plt.subplot(gs[3:,0])
quad2 = ax3.pcolormesh(x, y, z,shading='gouraud')
ax3.set_xlabel('time')
ax3.set_ylabel('amplitude')
cb3 = fig.colorbar(quad2,ax=ax3)

############################
ax4 = plt.subplot(gs[:,1])
line2, = ax4.plot(v,tt,'b',linewidth=2)
ax4.set_xlim(-10,10)
ax4.set_ylim(0,1)

def init():
    line.set_data([],[])
    line2.set_data([],[])
    quad1.set_array([])
    return line,line2,quad1

def animate(iter):
    t = np.sin(2*v-iter/(2*np.pi))*np.sin(2*v-iter/(2*np.pi))
    tt = np.cos(2*v-iter/(2*np.pi))*np.cos(2*v-iter/(2*np.pi))
    z = np.sin(x-iter/(2*np.pi))*np.sin(x-iter/(2*np.pi))+np.sin(y)*np.sin(y)
    line.set_data(v,t)
    quad1.set_array(z.ravel())
    line2.set_data(v,tt)
    return line,line2,quad1

gs.tight_layout(fig)

anim = animation.FuncAnimation(fig,animate,frames=100,interval=50,blit=False,repeat=False)
plt.show()

print 'Finished!!'
AlexNoir
  • 729
  • 3
  • 8
  • 20
  • Thanks Alex. For those have trouble... when I tried setting the quad data with `set_array([])` for my implementation I received an error, which was solved by passing an ndarray via `set_array(numpy.array([])`. I also outlined other issues I had in [my solution to this problem](http://stackoverflow.com/a/22714873/943773). – ryanjdillon Mar 28 '14 at 14:00
  • 1
    Note that your solution works only because you use shading='gouraud'. Without this parameter or with shading='flat' you need to add the line `z = z[:-1, :-1]`. – lumbric Jul 18 '15 at 10:54
10

There is an ugly detail you need to take care when using QuadMesh.set_array(). If you intantiate your QuadMesh with X, Y and C you can update the values C by using set_array(). But set_array does not support the same input as the constructor. Reading the source reveals that you need to pass a 1d-array and what is even more puzzling is that depending on the shading setting you might need to cut of your array C.

Edit: There is even a very old bug report about the confusing array size for shading='flat'.

That means:

Using QuadMesh.set_array() with shading = 'flat'

'flat' is default value for shading.

# preperation
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
y = np.linspace(-10, 10, num=1000)
x = np.linspace(-10, 10, num=1000)
X, Y = np.meshgrid(x, y)
C = np.ones((1000, 1000)) * float('nan')

# intantiate empty plot (values = nan)
pcmesh = plt.pcolormesh(X, Y, C, vmin=-100, vmax=100, shading='flat')

# generate some new data
C = X * Y

# necessary for shading='flat'
C = C[:-1, :-1]

# ravel() converts C to a 1d-array
pcmesh.set_array(C.ravel())

# redraw to update plot with new data
plt.draw()

Looks like:

resulting graphic with shadig=flat

Note that if you omit C = C[:-1, :-1] your will get this broken graphic:

broken graphic with shading=flat

Using QuadMesh.set_array() with shading = 'gouraud'

# preperation (same as for 'flat')
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
y = np.linspace(-10, 10, num=1000)
x = np.linspace(-10, 10, num=1000)
X, Y = np.meshgrid(x, y)
C = np.ones((1000, 1000)) * float('nan')

# intantiate empty plot (values = nan)
pcmesh = plt.pcolormesh(X, Y, C, vmin=-100, vmax=100, shading='gouraud')

# generate some new data
C = X * Y

# here no cut of of last row/column!

# ravel() converts C to a 1d-array
pcmesh.set_array(C.ravel())

# redraw to update plot with new data
plt.draw()

If you cut off the last row/column with shade='gouraud' you will get:

ValueError: total size of new array must be unchanged
lumbric
  • 6,054
  • 6
  • 37
  • 48
  • The reason for this behaviour is that in `pcolormesh` the x-y coordinates are the *quadrilateral corners* of the coloured areas [see documentation](https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.pcolormesh.html), so the dimensions of `x` and `y` should be one larger than the data, otherwise the last row and column of the data are (silently) ignored (also mentioned in the documentation). This is different for `gouraud`, because here you specify the values (colours) of the corners, so x,y and the data should all have the same dimensions. – Thomas Kühn Sep 10 '18 at 14:10
2

I am not sure why your quad = ax.pcolormesh(X,Y,Z) function is giving an error. Can you post the error?

Below is what I would do to create a simple animation using pcolormesh:

import matplotlib.pyplot as plt
import numpy as np

y, x = np.meshgrid(np.linspace(-3, 3,100), np.linspace(-3, 3,100))

z = np.sin(x**2+y**2)
z = z[:-1, :-1]

ax = plt.subplot(111)

quad = plt.pcolormesh(x, y, z)

plt.colorbar()

plt.ion()
plt.show()

for phase in np.linspace(0,10*np.pi,200):
    z = np.sin(np.sqrt(x**2+y**2) + phase)
    z = z[:-1, :-1]

    quad.set_array(z.ravel())
    plt.title('Phase: %.2f'%phase)
    plt.draw()

plt.ioff()
plt.show()

One of the frames: enter image description here

Does this help? If not, maybe you can clarify the question.

DanHickstein
  • 6,196
  • 12
  • 50
  • 85
  • Thank you. Your answer is helpful. It is the way I tried first (iterating the plot inside a loop), but then I saw people recommending the animation package of pyplot. I see that you use pot.ion() and pot.ioff(). You also use `set_array(z.ravel())` with one argument, z, not x,y,z. What is ravel() ? – AlexNoir Sep 14 '13 at 11:44
  • 1
    Ravel takes the 2D array and makes it 1D: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ravel.html – DanHickstein Sep 14 '13 at 15:57
  • And are you saying that you want to update the quad object with X,Y,and Z for each iteration? I was assuming that X and Y were staying the same. I'm not sure if you can update all at once. An alternate approach might be to use scipy.interpolate.griddata to generate a regular grid which could be plotted using imshow. – DanHickstein Sep 14 '13 at 16:55
  • Thank you, you solve my problem. At the end, all I need is to add `set_array(z.ravel())` , because I did not know that `set_array()` only accepts 1D arrays! I am going to post my solution using FuncAnimation. – AlexNoir Sep 15 '13 at 01:11
0

There is another answer presented here that looks simpler thus better (IMHO)

Here is a copy & paste of the alternative solution :

import matplotlib.pylab as plt
from matplotlib import animation

fig = plt.figure()

plt.hold(True)
#We need to prime the pump, so to speak and create a quadmesh for plt to work with
plt.pcolormesh(X[0:1], Y[0:1], C[0:1])

anim = animation.FuncAnimation(fig, animate, frames = range(2,155), blit = False)

plt.show()
plt.hold(False)

def animate( self, i):
    plt.title('Ray: %.2f'%i)
    #This is where new data is inserted into the plot.
    plt.pcolormesh(X[i-2:i], Y[i-2:i], C[i-2:i])
Gael Lorieul
  • 2,746
  • 2
  • 21
  • 47