Python, Matplotlib, plot multi-lines (array) and a

2019-02-17 17:45发布

问题:

I'm starting programming in Python (and OOP), but I have a solid experience in Fortran (90/95) and Matlab programming.

I'm developing a little tool using animation on tkinter environment. The goal of this tool is to animate multi-lines (an array and not a vector of data). Below, a simple example of my problem. I don't understand why the result of these two ways of plotting data are so different ?

from pylab import *

Nx=10
Ny=20

xx   = zeros( ( Nx,Ny) )
data = zeros( ( Nx,Ny) )

for ii in range(0,Nx):
    for jj in range(0,Ny):
        xx[ii,jj]   = ii
        data[ii,jj] = jj


dline = plot(xx,data)

mline, = plot([],[])
mline.set_data(xx.T,data.T)

show()

If you plot only "dline" each line is plotted separately and with a different color. If you plot only "mline" all the lines are linked and with only one color.

My goal is to make an animation with "mline" changing the data at each loop. Here a simple source code illustrating my purposes :

from pylab import *
from matplotlib import animation

Nx=10
Ny=20

fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)

ax = plt.axes(xlim=(0, Nx), ylim=(0, Ny))

xx   = zeros( ( Nx,Ny) )
data = zeros( ( Nx,Ny) )
odata = zeros( ( Nx,Ny) )

for ii in range(0,Nx):
    for jj in range(0,Ny):
        xx[ii,jj]    = ii
        odata[ii,jj] = jj
        data[ii,jj]  = 0.

#dline = plot(xx,odata)

mline, = plot([],[])

def init():
    mline.set_data([],[])
    return mline,

def animate(coef):
   for ii in range(0,Nx):
        for jj in range(0,Ny):
            data[ii,jj] = odata[ii,jj] * (1.-float(coef)/360.)

   mline.set_data(xx.T,data.T)
   return mline,

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

plt.show()

I hope that I have clearly exposed my problem.

Thanks, Nicolas.

回答1:

as @Rutger Kassies points out in the comments,

dline = plot(xx,data)

does some magic parsing on the input data, separates your arrays into a bunch of x-y pairs and plots those. Note that dline is a list of Line2D objects. In this case

mline, = plot([],[])
mline.set_data(xx.T,data.T)

you are creating a single Line2D object and the library does it's best to shove 2D data, into a 1D plotting objects and does so by flattening the input.

To animate N lines, you just need N Line2D objects:

lines = [plot([],[])[0] for j in range(Ny)] # make a whole bunch of lines

def init():
    for mline in lines:
        mline.set_data([],[])
    return lines

def animate(coef):
   data = odata * (1.-float(coef)/360.)
   for mline, x, d in zip(lines, data.T, xx.T):
       mline.set_data(x, d)
   return lines

You also don't need to pre-allocate data and doing the loops in python is much slower than letting numpy do them for you.



回答2:

Many thanks to Rutger Kassies and tcaswell. Here the same example as above but now it works as I want. I hope that it will help another python programmers.

from pylab import *
from matplotlib import animation

Nx=10
Ny=20

fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)
axis([0, Nx-1, 0, Ny])

xx    = zeros( ( Nx,Ny) )
data  = zeros( ( Nx,Ny) )
odata = zeros( ( Nx,Ny) )

for ii in range(0,Nx):
    xx[ii,:]    = float(ii)

for jj in range(0,Ny):
    odata[:,jj] = float(jj)

#dline = plot(xx,odata)

lines = [plot([],[])[0] for j in range(Ny)] # make a whole bunch of lines


def init():
    for mline in lines:
        mline.set_data([],[])
    return lines

def animate(coef):

   data = odata * (1.-float(coef)/360.)

   for mline, x, d in zip(lines, xx.T, data.T,):
       mline.set_data(x, d)

   return lines

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

plt.show()