Search code examples
pythonmatplotlibmatplotlib-animation

Animating subplots mixing matrices and line plots


I am writing a project in python that includes animation with matplotlib. The thing is I have to visualize a window divided into two rows and two columns, so 4 subplots. In ax[0,0], ax[0,1] and ax[1,0] I have matrices. In ax[1][1] a 2D line plot. The animation of the subplots with the matrices are working properly, but the line one does not.

The error is:

list object has no attribute set_data

The code is really long, so I will do a schematization. Could you please tell me what I did wrong?

rows, cols = 2, 3
fig, ax = plt.subplots(nrows=rows, ncols=cols, num=None, figsize=(16, 12), dpi=80, facecolor='w', edgecolor='k')

# Initialization of matrices MATR1, MATR2, MATR3 all cells set to 0 #

# Coordinates lists for the line plot
x_time = []
y_values = []

index = count()

im = list()
im.append(ax[0][0].imshow(MATR1, cmap='Reds', animated=True))
im.append(ax[0][1].imshow(MATR2, cmap='Greens', animated=True))
im.append(ax[1][0].imshow(MATR3, animated=True))
im.append(ax[1][1].plot(x_time, y_values, animated=True))

def animate(i):
    ax[1][1].cla()
    
    # Here a lot of code that determines the values that are inserted into the matrices and the value
    # to add in the y_values coordinates list for the line #
    
    x_time.append(next(index))
    y_values.append(len(ind_list)) 
    
    im[0].set_array(MATR1)
    im[1].set_array(MATR2)
    im[2].set_array(MATR3)
    im[4].set_data(x_time, y_erbpopulation) # ERROR FROM HERE

    return im

ani = FuncAnimation(fig, update, frames=NUMDAYS-1, interval=10, blit=True, repeat=False) 

Updated question:

After making some of the suggested changes, my animation is still not showing. See my updated code below.

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np

fig, ax = plt.subplots(2,2, num=None, figsize=(16, 12), dpi=80, facecolor='w', edgecolor='k')

MATR1 = np.zeros((100, 100))
MATR2 = np.zeros((100, 100))
MATR3 = np.zeros((100, 100))

# Coordinates lists for the line plot
x_time = []
y_values = []


im = list()
im.append(ax[0][0].imshow(MATR1, cmap='Reds', animated=True))
im.append(ax[0][1].imshow(MATR2, cmap='Greens', animated=True))
im.append(ax[1][0].imshow(MATR3, animated=True))
im.append(ax[1][1].plot(x_time, y_values, animated=True)[0])

def animate(i):
    ax[1][1].cla()
    
    # Here a lot of code that determines the values that are inserted into the matrices and the value
    # to add in the y_values coordinates list for the line, let's say now i+5 #
    
    x_time.append(i)
    y_values.append(i+5) 
    
    im[0].set_array(MATR1)
    im[1].set_array(MATR2)
    im[2].set_array(MATR3)
    im[4].set_data(x_time, y_values)

    return im

ani = FuncAnimation(fig, update, frames=100, interval=10, blit=True, repeat=False) 

Solution

  • If you look at the matplotlib plotting documentation you'll see that ax.plot returns a list of Line2D objects. In this case, that list contains just a single object. But, you're appending the list to im when you only want the Line2D object. So, you simply need to index the first value of the return as so:

    im.append(ax[1][1].plot(x_time, y_values, animated=True)[0])
    

    Updated solution:

    1. The use of animated=True is not what you think, it's for manually created animations with blotting (see here). So, those should be removed.
    2. You don't need ax[1,1].cla(). Using set_data will handle the update.
    3. You need to set the axes limits because the default will be out of range (matplotlib doesn't know the extent when you made the plot).
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    import numpy as np
    
    plt.close("all")
    
    fig, ax = plt.subplots(2, 2, figsize=(16, 12),)
    
    MATR1 = np.random.random((100, 100))
    MATR2 = np.random.random((100, 100))
    MATR3 = np.random.random((100, 100))
    
    x_time = []
    y_values = []
    
    
    im = []
    im.append(ax[0][0].imshow(MATR1, cmap='Reds'))
    im.append(ax[0][1].imshow(MATR2, cmap='Greens'))
    im.append(ax[1][0].imshow(MATR3))
    im.append(ax[1][1].plot(x_time, y_values)[0])
    
    ax[1,1].set_xlim(0, 100)
    ax[1,1].set_ylim(5, 105)
    
    def animate(i):    
        x_time.append(i)
        y_values.append(i+5) 
        
        im[0].set_array(MATR1)
        im[1].set_array(MATR2)
        im[2].set_array(MATR3)
        im[3].set_data(x_time, y_values)
    
        return im
    
    ani = FuncAnimation(fig, animate, frames=100, interval=10, blit=True, repeat=False)