Search code examples
pythonmatplotlib-animation

How to animate multiple arrays using matplotlib


I am attempting to show a sort of plot refresh on a sequence of array. Each array is an instance of the sequence and for each of them I want to plot the relative array, plus its smoothed version, like that: these frames should be showed in sequence thanks to the animation function Anyway the following error " AttributeError: 'list' object has no attribute 'get_zorder' " arises when I return the relative lines from the animation function. I tried solutions from other questions and channels but nothing really worked. Here is the code:

import random

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
from scipy.signal import savgol_filter

n_instances = 1000  # the instances that will be generated
instances_duration = 100  # the duration of each of them

# starting the data matrix
process_instances = np.zeros((n_instances, instances_duration))
# starting the filtered data matrix
filtered_instances = np.zeros((n_instances, instances_duration))

np.random.seed(2)
for i in range(n_instances):
    # creating the instance as a random array
    current_instance = np.random.normal(0, 1, instances_duration)
    # assigning to the relative matrix
    process_instances[i, :] = current_instance
    # filtering and assigning to the relative matrix
    filtered_instances[i, :] = savgol_filter(current_instance, 11, 3)

# managing the plots
fig, axs = plt.subplots()
axs.set_ylim([-3, 3])
axs.set_xlim([0, instances_duration])
axs.grid(True)
lines = axs.plot(process_instances[0, :], alpha=0.3, label='original')  # starting the main lines
lines_filt = axs.plot(filtered_instances[0, :], label='filtered')  # starting the filtered lines
axs.legend()


def animate(frame):
    # updating lines
    for columns, line in enumerate(lines):
        line.set_ydata(process_instances[frame, :])
    # updating filtered lines
    for columns, line in enumerate(lines_filt):
        line.set_ydata(filtered_instances[frame, :])
    print("Showing frame number: " + str(frame))
    return [lines, lines_filt]


animation = FuncAnimation(fig, animate, interval=1000, blit=True, repeat=True)
animation.event_source.start()
plt.show()

Solution

  • Only minor changes are necessary. Mainly, you have to unpack the Line2D objects for the animation loop

    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    import numpy as np
    from scipy.signal import savgol_filter
    
    n_instances = 1000  # the instances that will be generated
    instances_duration = 100  # the duration of each of them
    
    # starting the data matrix
    process_instances = np.zeros((n_instances, instances_duration))
    # starting the filtered data matrix
    filtered_instances = np.zeros((n_instances, instances_duration))
    
    np.random.seed(2)
    for i in range(n_instances):
        # creating the instance as a random array
        current_instance = np.random.normal(0, 1, instances_duration)
        # assigning to the relative matrix
        process_instances[i, :] = current_instance
        # filtering and assigning to the relative matrix
        filtered_instances[i, :] = savgol_filter(current_instance, 11, 3)
    
    # managing the plots
    fig, axs = plt.subplots()
    axs.set_ylim([-3, 3])
    axs.set_xlim([0, instances_duration])
    axs.grid(True)
    #unpack the Line2D artists
    lines, = axs.plot(process_instances[0, :], alpha=0.3, label='original')  # starting the main lines
    lines_filt, = axs.plot(filtered_instances[0, :], label='filtered')  # starting the filtered lines
    axs.legend()
    
    
    def animate(frame):
        # updating lines
        lines.set_ydata(process_instances[frame, :])
        # updating filtered lines
        lines_filt.set_ydata(filtered_instances[frame, :])
        print("Showing frame number: " + str(frame))
        
        #return the Line2D artists for blitting
        return lines, lines_filt,
    
    
    animation = FuncAnimation(fig, animate, interval=1000, blit=True, repeat=True)
    animation.event_source.start()
    plt.show()