Module 3 · Section 7 of 11
Lesson 3.6 - Curve Fitting
Target: ~10 min read - 20 min hands-on
Overview
scipy.optimize.curve_fit fits a chosen model (power, exponential, or any custom
function) to noisy data by finding the parameters that minimize the gap between model
and observations. We'll fit a first-order step response - the shape you see when a
temperature sensor, an RC circuit, a tank level, or a thermal mass settles toward a new
steady value.
Why This Matters (Engineering Context)
Extracting a time constant tau from step-test data is exactly how you characterize a
sensor's lag, tune a control loop, or size a thermal system - curve_fit automates the
fit.
Code-Along
from scipy.optimize import curve_fit
import numpy as np
np.random.seed(11)
# Generate synthetic step-response data: y(t) = y_inf * (1 - exp(-t / tau))
t_s = np.array([0, 1, 2, 4, 7, 12, 18, 25, 35, 50])
y_inf_true, tau_true = 42.0, 9.0
y_true = y_inf_true * (1 - np.exp(-t_s / tau_true))
y_obs = y_true + np.random.normal(0, 1.2, size=len(t_s)) # add measurement noise
# The model: first arg is the x data, the rest are the parameters to fit
def first_order(t, y_inf, tau):
return y_inf * (1 - np.exp(-t / tau))
# curve_fit finds the (y_inf, tau) that best match y_obs. p0 = starting guess.
# It returns (best-fit params, covariance matrix); "_" discards the covariance.
params, _ = curve_fit(first_order, t_s, y_obs, p0=[40, 10])
y_inf_fit, tau_fit = params
print(f"Fitted y_inf: {y_inf_fit:.2f} (true {y_inf_true})")
print(f"Fitted tau: {tau_fit:.2f} s (true {tau_true})")
t_query = 90
# *params unpacks the tuple into first_order's (y_inf, tau) arguments
print(f"\nPredicted response at {t_query} s: {first_order(t_query, *params):.2f}")
# R-squared = 1 - (sum of squared residuals) / (total variance of y); 1.0 = perfect
resid = y_obs - first_order(t_s, *params)
r2 = 1 - np.sum(resid ** 2) / np.sum((y_obs - np.mean(y_obs)) ** 2)
print(f"R-squared: {r2:.4f}")
Run it: the fitted y_inf and tau land close to the true generating values
(42.0 and 9.0) despite the added noise - that recovery is the whole point of curve
fitting. R-squared near 1.0 indicates a strong fit; a low value would mean the wrong
model form.
Practice Exercises
- Fit a power-law model
y = a * x**bto a synthetic dataset (x = np.linspace(1, 20, 15),y = 3.5 * x**0.6 + noise) and recoveraandb. - Increase the noise (
scale=3.0instead of1.2) and observe how far the fitted parameters drift from the true values. - Use the fitted
tauto estimate how long it takes to reach 95% ofy_inf: solve0.95 = 1 - exp(-t/tau).
# Try the practice exercises here
Knowledge Check
- What does
curve_fitreturn? - Why is an initial guess (
p0) sometimes necessary forcurve_fitto converge correctly? - What does an R^2 near 1.0 indicate about a fitted curve?
Answer key
- The best-fit parameter values (and a covariance matrix describing their uncertainty)
- Non-linear optimization can converge to a wrong local solution, or fail, without a reasonable start
- The model explains most of the variance in the observed data - a strong fit
29 / 63 sections · Course home · Join the coaching cohort