I'd like to plot an animation of Lissajous curves using Python and matplotlib's animate library. I really do not have a lot of experience with Python, so rather than performance increasements, I'm looking for best practices to improve (and/or shorten) my code.

The following code produces a .gif file when evaluated as a jupyter-lab cell:

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

x_data = []
y_data = []
max_range = 1.2

f1 = 3        # sets the frequency for the horizontal motion
f2 = 5        # sets the frequency for the vertical motion
d1 = 0.0      # sets the phase shift for the horizontal motion
d2 = 0.5      # sets the phase shift for the vertical motion
delta1 = d1 * np.pi # I define the phase shift like this in order to use 
delta2 = d2 * np.pi # ...d1 and d2 in the export file name

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(4,4), gridspec_kw={'width_ratios': [6, 1], 'height_ratios': [1, 6]})

for i in [ax1, ax2, ax3, ax4]: 
    i.set_yticklabels([])         # in order to remove the ticks and tick labels
    i.set_xlim(-max_range, max_range)
    i.set_ylim(-max_range, max_range)

line, = ax3.plot(0, 0)        # line plot in the lower left
line2 = ax3.scatter(0, 0)     # moving dot in the lower left
linex = ax1.scatter(0, 0)     # moving dot on top
liney = ax4.scatter(0, 0)     # moving dot on the right

def animation_frame(i):
    ax1.clear()              # I tried to put this in a loop like this: 
    ax1.set_yticklabels([])  # for i in [ax1,ax3,ax4]: 
    ax1.set_xticklabels([])  #     i.clear() 
    ax1.set_xticks([])       #     (... etc.)
    ax1.set_yticks([])       # but this didn't work
    ax1.set_xlim(-max_range, max_range)     # after ax.clear() I apparently have to re-set these
    ax3.set_xlim(-max_range, max_range)
    ax3.set_ylim(-max_range, max_range)
    ax4.set_ylim(-max_range, max_range)
    x_data.append(np.sin(i * f1 + delta1)) # for the line plot
    y_data.append(np.sin(i * f2 + delta2))
    x_inst = np.sin(i * f1 + delta1)       # for the scatter plot
    y_inst = np.sin(i * f2 + delta2)
    line, = ax3.plot(x_data, y_data)
    line2 = ax3.scatter(x_inst, y_inst)
    linex = ax1.scatter(x_inst, 0)
    liney = ax4.scatter(0, y_inst)
    transFigure = fig.transFigure.inverted()       # in order to draw over 2 subplots
    coord1 = transFigure.transform(ax1.transData.transform([x_inst, 0]))
    coord2 = transFigure.transform(ax3.transData.transform([x_inst, y_inst]))
    my_line1 = matplotlib.lines.Line2D((coord1[0],coord2[0]),(coord1[1],coord2[1]), transform=fig.transFigure, linewidth=1, c='gray', alpha=0.5)
    coord1 = transFigure.transform(ax3.transData.transform([x_inst, y_inst]))
    coord2 = transFigure.transform(ax4.transData.transform([0, y_inst]))
    my_line2 = matplotlib.lines.Line2D((coord1[0],coord2[0]),(coord1[1],coord2[1]), transform=fig.transFigure, linewidth=1, c='gray', alpha=0.5)
    fig.lines = my_line1, my_line2,         # moving vertical and horizontal lines
    return line, line2, linex, liney

animation = FuncAnimation(fig, func=animation_frame, frames=np.linspace(0, 4*np.pi, num=800, endpoint=True), interval=1000)
animation.save('lissajous_{0}_{1}_{2:.2g}_{3:.2g}.gif'.format(f1,f2,d1,d2), writer='pillow', fps=50, dpi=200)
# This takes quite long, but since I'd like to have a smooth, slow animation, 
# ...I'm willing to accept a longer execution time. 


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.