Fitting exponential decay curve
# Fitting expoential decay function
Expoential Decay Function:
$y = A e^{Bx}$
Take the log of both sides:
$log(y) = log(A) + Bx$
Model log(y) as a linear function to figure out the slope B and the intercept log(A).<br>
Numpy.polyfit(x, y) Least squares polynomial fit.<br>
import numpy as np
x = np.array([10, 19, 30, 35, 51])
y = np.array([79, 50, 20, 7, 1])
result = np.polyfit(x, np.log(y), deg=1)
print(result)
B = result[0]
log_A = result[1]
A = np.exp(log_A)
X = np.arange(0, 60, 1)
y1 = A * np.exp(B*X)
print('The curve is : y = {} * e^({}x)'.format(A, B))
[-0.10943844 5.81833736]
The curve is : y = 336.4122549507605 * e^(-0.10943843796666908x)
The problem is with a large y, the least square |y - y'|^2 creates a lot of bias towards small y values. A larger y value dominiates the loss function as opposed to a smaller y. One way to remediate is to add weights, so as to increase the weight of a smaller y and decrease the weight of a bigger y.<br>
**Numpy.polyfit(x, y, w)** Weighted Least squares polynomial fit.<br>
w:<br>
Weights to apply to the y-coordinates of the sample points. For gaussian uncertainties, use 1/sigma (not 1/sigma**2).
weight = np.sqrt(y)
result = np.polyfit(x, np.log(y), deg=1, w=np.sqrt(y))
print(result)
B = result[0]
log_A = result[1]
A = np.exp(log_A)
X = np.arange(0, 60, 1)
y2 = A * np.exp(B*X)
print('The curve is : y = {} * e^({}x)'.format(A, B))
[-0.08022606 5.2598691 ]
The curve is : y = 192.45629707026794 * e^(-0.08022606279569525x)
# Fitting expoential decay with a bias
$y = A e^{Bx} + C$
The obove method will not work as the term C breaks the nice property of log. Other fitting method is needed. Scipy has a nice curve_fit that uses non-linear least squares to fit a function, f, to data.
import scipy.optimize
def fn(x, A, B, C):
return A * np.exp(-B*x) + C
params, cv = scipy.optimize.curve_fit(fn, x, y)
X = np.arange(0, 60, 1)
y3 = fn(X, *params)
print(params)
[ 1.57265984e+02 5.22201371e-02 -1.27363118e+01]
Compare the fitted curves
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 8))
plt.scatter(x, y, c='green')
plt.plot(X, y1, c='red', label='polyfit')
plt.plot(X, y2, c='blue', label='polyfit with weight')
plt.plot(X, y3, c='orange', label='optimize.curve_fit')
plt.legend(loc='upper right')
plt.show()