I'm trying to animate the Taylor series from 1 through 50 terms. The code I currently have allows me to specify a number of terms and plots it onto a graph (with another graph for comparison):
import math
import numpy as np
import matplotlib.pyplot as plt
def func_cos(x, n):
cos_approx = 0
for i in range(n):
coef = (-1)**i
num = x**(2*i)
denom = math.factorial(2*i)
cos_approx += ( coef ) * ( (num)/(denom) )
return cos_approx
def func_sin(x, n):
sin_approx = 0
for i in range(n):
coef = (-1)**i
num = x**(2*i+1)
denom = math.factorial(2*i+1)
sin_approx += ( coef ) * ( (num)/(denom) )
return sin_approx
i = 1j
e = math.e
x = np.linspace(0, 10, 1000)
fx = e**(i*x)
taylor = lambda j: np.array([func_cos(x_val,j) + i*func_sin(x_val,j) for x_val in x])
plt.plot(fx.real, fx.imag)
plt.plot(taylor(50).real, taylor(50).imag)
plt.show()
Is there a way to animate the graph so that the line changes equation every frame to that number of Taylor series terms?
One way is animation
module. It is probably the best method in such a case, since you have a clear definition of what ith frame should look like.
Here is your code, updated with some commented lines.
import math
import numpy as np
import matplotlib.pyplot as plt
# You need this module
import matplotlib.animation as anim
def func_cos(x, n):
cos_approx = 0
for i in range(n):
coef = (-1)**i
num = x**(2*i)
denom = math.factorial(2*i)
cos_approx += ( coef ) * ( (num)/(denom) )
return cos_approx
def func_sin(x, n):
sin_approx = 0
for i in range(n):
coef = (-1)**i
num = x**(2*i+1)
denom = math.factorial(2*i+1)
sin_approx += ( coef ) * ( (num)/(denom) )
return sin_approx
i = 1j
e = math.e
x = np.linspace(0, 10, 1000)
fx = e**(i*x)
taylor = lambda j: np.array([func_cos(x_val,j) + i*func_sin(x_val,j) for x_val in x])
plt.plot(fx.real, fx.imag)
# You need to keep the "artist", that is the plotted line, to be able to update its data later
# Note the `,`: plt.plot returns 2 things, the first being the one we want (pltdata)
pltdata,=plt.plot(taylor(0).real, taylor(0).imag)
# The animate function is in charge of updating the data
# i is the frame number
def animate(i):
pltdata.set_data(taylor(i).real, taylor(i).imag)
# Animate returns the list of the "artists" it has changed. So here, just [pltdata]
return [pltdata]
# Note the dummy "myanim" variable. Even if we don't use it afterward, it is necessary
# so that the garbage collector doesn't destry the animation
# plt.gcf() is just the figure. So, if you have a fig=plt.figure() somewhere, that should be replaced by `fig`
# I reduced the 50 values, because, well, 20 is more than enough (at this point, you even start adding
# more numerical noise that accuracy. But well, that has nothing to do with animation)
myanim = anim.FuncAnimation(plt.gcf(), animate, frames=20, interval=200, blit=True)
plt.show()
# or myanim.save('out.mp4') to create a animation in a file
Plus (see last, commented, line), animation
allows easy creation of cool animated gif (or mp4, but gif is needed to put in a [so] message), like this one
Another way is to make the plot interactive, and to update it (or even redraw everything from scratch each time, it is up to you), using your own timing strategy.
plt.ion()
for k in range(20):
plt.gcf().clear()
plt.xlim(-1.5, 1.5)
plt.ylim(-1.5, 1.5)
plt.plot(fx.real, fx.imag)
plt.plot(taylor(k).real, taylor(k).imag)
plt.draw()
plt.pause(0.2)
Or a mixture of both
plt.ion()
plt.plot(fx.real, fx.imag)
plt.xlim(-1.5, 1.5)
plt.ylim(-1.5, 1.5)
pltdata,=plt.plot(taylor(0).real, taylor(0).imag)
for k in range(20):
pltdata.set_data(taylor(k).real, taylor(k).imag)
plt.draw()
plt.pause(0.2)
Note that I wanted to focus only on animation part here. So no optimization specific to your case. For example, by computing taylor(k)
each times, taylor(k)
itself computing all terms, I am basically computing 30 times the constant coefficent, 29 times the x coefficient, 28 times the x² coefficient, ...
That, obviously could be optimized.
For example like this
plt.ion()
plt.plot(fx.real, fx.imag)
plt.xlim(-1.5, 1.5)
plt.ylim(-1.5, 1.5)
pltdata,=plt.plot(xx, yy)
fac=1.0
for k in range(20):
xx += (-1)**k * x**(2*k) / fac
fac *= (2*k+1)
yy += (-1)**k * x**(2*k+1) / fac
fac *= (2*k+2)
pltdata.set_data(xx,yy)
plt.draw()
plt.pause(0.2)