Numerical Differentiation#


Warning

This notebook is outdated and should only give you an idea about the topic. Do not use SciPy for numerical differentiation anymore.

# load the standard modules
import matplotlib.pylab as plt
import numpy as np

The Problem & Its Solution#

We aim to calculate:

\begin{align*} f’(x) &= \frac{df(x)}{dx} \ &\approx \frac{f(x+d) - f(x)}{d} \ &\approx \frac{f(x+d) - f(x-d)}{2d} \ \text{or} \quad f’’(x) &= \frac{d^2f(x)}{dx^2} \approx \dots \end{align*}

\(\Rightarrow\) Again, we could implement all on our own. Or we could use the predefined functions from Numpy and Numdifftools!

Numerical Differentiation of Analytic Functions#

def f(x):
    return np.sin(x)

def fp(x):
    return np.cos(x)

def fpp(x):
    return -np.sin(x)

def fppp(x):
    return -np.cos(x)

# and plot it
x = np.linspace(0, 2.0*np.pi, 100)

plt.figure(1)

plt.plot(x, f(x),    label="$f(x)    = sin(x)$")
plt.plot(x, fp(x),   label="$f'(x)   = cos(x)$")
plt.plot(x, fpp(x),  label="$f''(x)  = -sin(x)$")
plt.plot(x, fppp(x), label="$f'''(x) = -cos(x)$")

plt.xlabel("$x$")
plt.ylabel("$f(x)$")
plt.legend(loc=1)

plt.show()
../_images/8f9a51cd4333574c40fb9392b7e86a0cb9653604a9b2a1bc7e623b7cdd49723a.png
import numpy as np
import matplotlib.pyplot as plt
import numdifftools as nd

def f(x):  return np.sin(x)
def fp(x): return np.cos(x)

x0 = 1.23

# n — derivative order
df = nd.Derivative(f, n=1, step=1.5)(x0)
print('df = ', df)

x = np.linspace(0, 2*np.pi, 400)
plt.plot(x, fp(x),     label="$f'(x)=\\cos(x)$")
plt.plot(x0, df, 'ro', label=f"$f'_{{num}}(x={x0:.2f})$")
plt.xlabel("$x$")
plt.ylabel("$f'(x)$")
plt.legend(loc=1)
plt.show()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[3], line 3
      1 import numpy as np
      2 import matplotlib.pyplot as plt
----> 3 import numdifftools as nd
      5 def f(x):  return np.sin(x)
      6 def fp(x): return np.cos(x)

ModuleNotFoundError: No module named 'numdifftools'
x  = np.linspace(0, 2*np.pi, 400)
x0 = 1.23

# Compute numerical derivatives
df   = nd.Derivative(f, n=1, step=0.1)(x0)
ddf  = nd.Derivative(f, n=2, step=0.1)(x0)
dddf = nd.Derivative(f, n=3, step=0.1, order=3)(x0)

# Plot
plt.figure(1)

plt.plot(x, fp(x),   label="$f'(x)=\\cos(x)$")
plt.plot(x, fpp(x),  label="$f''(x)=-\\sin(x)$")
plt.plot(x, fppp(x), label="$f'''(x)=-\\cos(x)$")

plt.plot(x0, df,   'bo', label=f"$f'_{{num}}(x={x0:.2f})$")
plt.plot(x0, ddf,  'yo', label=f"$f''_{{num}}(x={x0:.2f})$")
plt.plot(x0, dddf, 'go', label=f"$f'''_{{num}}(x={x0:.2f})$")

plt.xlabel("$x$")
plt.ylabel("derivatives of $f(x)$")
plt.legend(loc=1)
plt.show()
../_images/83fa95ea3b6c0b46ef196e0e843c3843b393aef9ac9f88930800bbdc00905075.png

Numerical Differentiation of Sampled Data#

# define some sample data
xi = np.linspace(0, 6, 50)
yi = np.sin(xi/0.5) * np.exp(-xi)

plt.figure(1)
plt.plot(xi, yi, 'bo')

plt.xlabel("$x$")
plt.ylabel("$f(x)$")

plt.show()
../_images/358e156d6dca36f48482fccad3411496bdd166263974fdd3e66283a008198693.png

Strategy 1: Fit and use the fit-function in Scipy’s derivative#

import numpy as np
import matplotlib.pyplot as plt
import numdifftools as nd

# polynomial approximation
myFit = np.polyfit(xi, yi, deg=9)
fFit = np.poly1d(myFit)

x0 = 2.5

# First derivative
df0 = nd.Derivative(fFit, n=1, step=0.01)(x0)

df = nd.Derivative(fFit, n=1, step=0.01)(xi)

# Plotting
xfine = np.linspace(np.min(xi), np.max(xi), 200)

plt.plot(xi, yi, 'bo', label="$f(x)$")
plt.plot(xfine, fFit(xfine), 'r-', label="$f_{fit}(x)$")
plt.plot(xi, df, 'g-', label="$f'_{num}(x)$")
plt.text(2.0, 0.3, f"$f'({x0:.1f}) = {df0:.3f}$", fontsize=16)

plt.xlabel("$x$")
plt.ylabel("$f(x)$")
plt.legend()
plt.show()
../_images/ae30959bb7764ee15031fd5fbd25fa8bb850d57829a41d35820a52553d2980b9.png

Strategy 2: Using Numpy’s diff#

# Note: diff returns just f(x_i+1) - f(x_i)
#       so we need to divide by d = x_i - x_i+1 on our own

df = np.diff(yi) / (xi[1] - xi[0])

# find the index of xi which is closest to xx
# (the point at which we'd like to evaluate the differentiation)
ind = np.argmin(np.abs(xi - x0))

plt.text(2.0, 1.0, "$f'(%2.1f) = %4.3f$"%(x0,df[ind]), fontsize=16)

plt.plot(xi, yi, 'bo', label="$f(x)$")
plt.plot(xi[1:], df, 'g-', label="$f'_{num}(x)$") 

plt.xlabel("$x$")
plt.ylabel("$f(x)$")
plt.legend()

plt.show()
../_images/3f3440727bb94572c7ac44be4741b91a2d54a1a1c97f37e65e9a1651957c20d5.png