© 2018 Suzy Beeler. This work is licensed under a Creative Commons Attribution License CC-BY 4.0. All code contained herein is licensed under an MIT license

This exercise was generated from a Jupyter notebook. You can download the notebook here.


Objective

This tutorial will illustrate the concept of Taylor series expansion, where a given function can be approximated with a polynomial function. We will see that increasing the order of the polynomial results in ever better approximations.

In [1]:
# import the necessary modules
import numpy as np
import matplotlib.pyplot as plt

# allows us to compute factorials 
import math

# For pretty plots
import seaborn as sns
rc={'lines.linewidth': 2, 'axes.labelsize': 14, 'axes.titlesize': 14, \
    'xtick.labelsize' : 14, 'ytick.labelsize' : 14}
sns.set(rc=rc)

Taylor series expansion of $e^x$

The Taylor series expansion of $e^x$ is given by

$$e^x = 1 \ + \ x + \ \frac{x^2}{2!} \ + \ \frac{x^3}{3!} \ + \ \frac{x^4}{4!} \ + \ . \ .\ . \ + \ \frac{x^n}{n!}. \tag{1}$$

We see that each new term in this series takes on the form of $\frac{x^n}{n!}$. This means we can iteratively determine the Taylor series expansion for an increasing number of terms. That is, we can use the $0$th order approximation to get the $1$st order approximation, and we can use the $1$st order approximation to get the $2$nd order approximation, and so on. To do this, we can store our $y$ values in a 2D array, where the rows correspond to the order of the polynomial we are using and the columns correspond to increasing values of $x$ for which we are computing.

In [2]:
# max number of terms we want to expand to
n_terms = 5

# range of x values to look at
x_vals = np.linspace(0,2,100)

# intialize the array of  y_vals
y_vals = np.zeros([n_terms,len(x_vals)])

# loop through the number of terms
for n in range(n_terms):
    
    # special case for 0th order
    if n == 0:
        y_vals[n,:] = np.ones(len(x_vals))
      
    # otherwise nth order is n-1th order plus new term
    else:
        new_term = x_vals**n / math.factorial(n)
        y_vals[n,:] = y_vals[n-1,:] + new_term

We can now loop through our $y$ values array to plot our approximations for an increasing number of terms. We can also plot the real function, $e^x$, for comparison.

In [3]:
# loop through number of terms and plot
for n in range(n_terms):
    label = str(n) + " order"
    plt.plot(x_vals,y_vals[n,:], label=label)
    
# plot the real function  
plt.plot(x_vals, np.exp(x_vals), label="$e^x$")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
Out[3]:
<matplotlib.legend.Legend at 0x1a1cff07b8>

Nice! We see how increasing the order of our polynomial brings us ever closer to the true form of the $e^x$ function. Using this code a template, we can now compute the Taylor series expansion for a number of different functions. The only things we may need to change are the $0$th order term and the form of each new term.

Taylor series expansion of $cos(x)$

The Taylor series expansion of $cos(x)$ is given by

$$cos(x) = 1 \ - \ \frac{x^2}{2!} \ + \ \frac{x^4}{4!} \ - \ \frac{x^6}{6!} \ + \ \frac{x^8}{8!} \ - \ . \ . \ . \ + \ (-1)^n \frac{x^{2n}}{(2n)!}. \tag{2}$$

In [4]:
# max number of terms we want to expand to
n_terms = 5

# range of x values to look at
x_vals = np.linspace(0,2*np.pi,100)

# intialize the y_vals
y_vals = np.zeros([n_terms,len(x_vals)])

# loop through the number of terms
for n in range(n_terms):
    
    # special case for 0th order
    if n == 0:
        y_vals[n,:] = np.ones(len(x_vals))
      
    # otherwise nth order is n-1th order plus new term
    else:
        new_term = (-1)**n * x_vals**(2*n) / math.factorial(2*n)
        y_vals[n,:] = y_vals[n-1,:] + new_term
In [5]:
# loop through number of terms and plot
for n in range(n_terms):
    label = str(n) + " order"
    plt.plot(x_vals,y_vals[n,:], label=label)

# plot the real function    
plt.plot(x_vals, np.cos(x_vals), label="$cos(x)$")
plt.xlabel("x")
plt.ylabel("y")
plt.ylim([-1.1,1.1]) # set y-axis to make it easier to see
plt.legend()
Out[5]:
<matplotlib.legend.Legend at 0x1a1d96cf60>

Taylor series expansion of $ln(1+x)$

The Taylor series expansion of $ln(1+x)$ is given by

$$ \ln ( 1 + x ) = x \ - \ \frac { x ^ { 2 } } { 2 } \ + \ \frac { x ^ { 3 } } { 3 } \ - \ \frac { x ^ { 4 } } { 4 } \ + \ \frac { x ^ { 5 } } { 5 } \ - \ . \ . \ . \ + \ (-1)^{n} \frac{x^{n+1}}{n+1}. \tag{3}$$

In [6]:
# max number of terms we want to expand to
n_terms = 5

# range of x values to look at
x_vals = np.linspace(0,1,100)

# intialize the y_vals
y_vals = np.zeros([n_terms,len(x_vals)])

# loop through the number of terms
for n in range(n_terms):
    
    # special case for 0th order
    if n == 0:
        y_vals[n,:] = x_vals
      
    # otherwise nth order is n-1th order plus new term
    else:
        new_term = (-1)**n * x_vals**(n+1) / (n+1)
        y_vals[n,:] = y_vals[n-1,:] + new_term
In [7]:
# loop through number of terms and plot
for n in range(n_terms):
    label = str(n) + " order"
    plt.plot(x_vals,y_vals[n,:], label=label)

# plot the real function 
plt.plot(x_vals, np.log(1 + x_vals), label="$ln(1+x)$")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
Out[7]:
<matplotlib.legend.Legend at 0x1a1ddfb3c8>

Taylor series expansion of $\frac{1}{1+x}$

The Taylor series expansion of $\frac{1}{1+x}$ is given by

$$ \frac { 1 } { 1 + x } \ = \ 1 \ - \ x \ + \ x ^ { 2 } \ - \ x ^ { 3 } \ + \ x^4 \ - \ . \ . \ . \ + \ (-1)^n x^n. \tag{4} $$

In [8]:
# max number of terms we want to expand to
n_terms = 5

# range of x values to look at
x_vals = np.linspace(0,1,100)

# intialize the y_vals
y_vals = np.zeros([n_terms,len(x_vals)])

# loop through the number of terms
for n in range(n_terms):
    
    # special case for 0th order
    if n == 0:
        y_vals[n,:] = np.ones(len(x_vals))
      
    # otherwise nth order is n-1th order plus new term
    else:
        new_term = (-1)**n * (x_vals**n)
        y_vals[n,:] = y_vals[n-1,:] + new_term
In [9]:
# loop through number of terms and plot
for n in range(n_terms):
    label = str(n) + " order"
    plt.plot(x_vals,y_vals[n,:], label=label)

# plot the real function    
plt.plot(x_vals, 1/(1+x_vals), label="$1/(1+x)$")
plt.xlabel("x")
plt.ylabel("y")
plt.ylim([0,1.1])
plt.legend()
Out[9]:
<matplotlib.legend.Legend at 0x1a1deeccf8>