3

I plot information from a 2D array with pcolor. however, the information in the array is changed over the iterations, and I want to update the color map dynamically, in order to visualize the changes in real time. How can I do it in the most simple way?

Edit - example:

from __future__ import division
from pylab import *
import random

n = 50 # number of iterations
x = arange(0, 10, 0.1)
y = arange(0, 10, 0.1)
T = zeros([100,100]) # 10/0.1 = 100
X,Y = meshgrid(x, y)

"""initial conditions"""
for x in range(100):
 for y in range(100):
  T[x][y] = random.random()

pcolor(X, Y, T, cmap=cm.hot, vmax=abs(T).max(), vmin=0)
colorbar()
axis([0,10,0,10])
show() # colormap of the initial array

"""main loop"""

for i in range(n):
 for x in range(100):
  for y in range(100):
   T[x][y] += 0.1 # here i do some calculations, the details are not important

 # here I want to update the color map with the new array (T)

Thanks

7
  • possibly helpful 10944621 Commented Apr 13, 2013 at 20:29
  • Can you use imshow instead of pcolor? Commented Apr 13, 2013 at 20:30
  • Can you also post a minimal example of what you are doing and any (partially) working code? It makes in much easier to help you. Commented Apr 13, 2013 at 20:31
  • thanks, but I want to do it with the "pcolor" command. I can also use imshow. Commented Apr 13, 2013 at 20:33
  • 1
    why? imshow has an almost trivial method for updating the data, pcolor returns a PolyCollection which is a bit harder to update Commented Apr 13, 2013 at 20:34

3 Answers 3

3

I have here a simple example how to update ax.pcolor (or rather its faster cousin ax.pcolormesh) during a simulation.

def make_movie(fig, meshData, conc, fout='writer_test.mp4',
           dpi=150, metadata={}):
    '''
    Make a movie (on disk) starting from a first image generated with matplotlib,
    by updating only the values that were dispayed with ax.pcolormesh(...).

    Parameters
    ----------
    meshData: mesh as returned by ax.pcolormesh()
    conc: obj returned by readUCN
        computed concentrations
    fout: str
        name of output file, with or without '.mp4' extension.
    dpi: int
        dots per inch of output movie
    metadata: dict
        passed on to FFMpegWriter.savings(fout, ...)
    '''
    plt.rcParams['animation.ffmpeg_path'] = '/usr/local/bin/ffmpeg'

    from matplotlib.animation import FFMpegWriter

    writer = FFMpegWriter(fps=15, metadata=metadata)

    totims = conc.totim # get times of computed concentrations

    with writer.saving(fig, fout, dpi):
        for totim in totims:
            C = conc.at_t(totim)[:, 0, :] # 3D -->  2D Xsection concentrations
            #newcolors = cmap(norm(C.ravel()))
            #meshData.update({'facecolors': newcolors})
            meshData.update({'array': C.ravel()}) # reset array to new conc.
            fig.canvas.draw_idle()
            writer.grab_frame()

The lines starting with #newcolors and #meshData.update are as suggested by @tacaswell above. The line starting with meshdata.udate({array ... replaces them. It just updates the data without computing the new facecolors. The last method is simpler and works just as well. Transposing the data array is not necessary.

Sign up to request clarification or add additional context in comments.

1 Comment

All you need is meshData.update({"array":C.ravel()}) (and some sort of draw call) but this should be the answer people use and refer to now. FWIW, I ran some basic timing and the update function is ~50-100x faster than the original pcolormesh call for my simple 100x100 test case. A better answer might be simplified to remove the extra movie related code work (though I learned from the movie code too, so thanks for it.)
2

I would suggest using imshow (doc):

# figure set up
fig, ax_lst = plt.subplots(2, 1)
ax_lst = ax_lst.ravel()

#fake data
data = rand(512, 512)
x = np.linspace(0, 5, 512)
X, Y = meshgrid(x, x)

data2 = np.sin(X ** 2 + Y **2)
# plot the first time#fake data

im = ax_lst[0].imshow(data, interpolation='nearest', 
                            origin='bottom', 
                            aspect='auto', # get rid of this to have equal aspect
                            vmin=np.min(data),
                            vmax=np.max(data), 
                            cmap='jet')

cb = plt.colorbar(im)

pc = ax_lst[1].pcolor(data)
cb2 = plt.colorbar(pc)

To updata the data with imshow, just set the data array, and it takes care of all of the normalization and color mapping for you:

# update_data (imshow)
im.set_data(data2) 
plt.draw()

To do the same with thing with pcolor you need to do the normalization and color mapping your self (and guess the row-major vs column major right):

my_cmap = plt.get_cmap('jet')
#my_nom = # you will need to scale your read data between [0, 1]
new_color = my_cmap(data2.T.ravel())
pc.update({'facecolors':new_color})
   
draw() 

6 Comments

Thanks are good, upvote and accept are better ;) (also see edit re colorbar)
last question: How can i limit the the x and y axes? imshow does not allow me to write imshow(X,Y,T) but only imshow(T).
You want to use the extent kwarg
hmmm...you said that imshow should do the color mapping for me. Howerver it does not happen, the colors do not change over time. Should I write some command in order to update it?
@user1767774 You might need a plt.pause() in your loop to make sure the canvas actually updates. Other than that, post a new with what code you are using.
|
1

You can connect events to your figure and call a specific function on that event. In the following I took an example of the matplotlib documentation and added a function ontype. This is called when 1 is pressed on the keyboard. Then X * func3() is called. Ontype is bound to the figure with fig.canvas.mpl_connect('key_press_event',ontype). In a similar way you could fire regular events time dependent.

#!/usr/bin/env python
"""
See pcolor_demo2 for an alternative way of generating pcolor plots
using imshow that is likely faster for large grids
"""
from __future__ import division
from matplotlib.patches import Patch
from pylab import *

def ontype(event):
    ''' function that is called on key event (press '1')'''
    if event.key == '1':
        print 'It is working'
        fig.gca().clear()
        # plot new function X * func3(X, Y) 
        Z = X * func3(X, Y) 
        pcolor(X, Y, Z, cmap=cm.RdBu, vmax=abs(Z).max(), vmin=-abs(Z).max())
        fig.canvas.draw()

def func3(x,y):
    return (1- x/2 + x**5 + y**3)*exp(-x**2-y**2)


# make these smaller to increase the resolution
dx, dy = 0.05, 0.05

x = arange(-3.0, 3.0001, dx)
y = arange(-3.0, 3.0001, dy)
X,Y = meshgrid(x, y)

Z = func3(X, Y)

fig=figure(figsize=(16,8))

# connect ontype to canvas
fig.canvas.mpl_connect('key_press_event',ontype)

pcolor(X, Y, Z, cmap=cm.RdBu, vmax=abs(Z).max(), vmin=-abs(Z).max())
colorbar()
axis([-3,3,-3,3])

show()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.