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

user1767774
  • 1,775
  • 3
  • 24
  • 32

3 Answers3

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.

theo olsthoorn
  • 455
  • 3
  • 6
  • 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.) – Ethan Gutmann Feb 15 '20 at 17:38
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() 
tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • 1
    Thanks are good, upvote and accept are better ;) (also see edit re colorbar) – tacaswell Apr 13 '13 at 21:20
  • 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). – user1767774 Apr 13 '13 at 21:26
  • You want to use the `extent` kwarg – tacaswell Apr 13 '13 at 21:33
  • 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 Apr 13 '13 at 22:39
  • @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. – tacaswell Apr 14 '13 at 02:39
  • http://stackoverflow.com/questions/13595566/matplotlib-qt-imshow-animate/13802936#13802936 – tacaswell Apr 14 '13 at 02:41
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()
Holger
  • 2,125
  • 2
  • 19
  • 30