Nonlinear measurements rarely reveal model parameters directly because observed values combine the underlying response with sampling noise. scipy.optimize.curve_fit() estimates the parameters by minimizing squared residuals between the measurements and a model function.
The model callable receives the independent variable first and the parameters to estimate afterward. For an exponential decay, amplitude, decay rate, and baseline form a compact model that can be constrained to nonnegative values.
The fitting call returns parameter estimates and an approximate covariance matrix. Residual RMSE shows how closely the fitted curve follows the measurements, while standard errors and the covariance condition number expose uncertainty or redundant parameter behavior.
import numpy as np from scipy.optimize import curve_fit def decay_model(x, amplitude, decay, baseline): return amplitude * np.exp(-decay * x) + baseline x = np.linspace(0.0, 4.0, 9) y = np.array( [2.91, 1.77, 1.15, 0.82, 0.62, 0.51, 0.44, 0.41, 0.39], dtype=float, )
initial_guess = (2.5, 1.0, 0.3) bounds = (0.0, [5.0, 5.0, 2.0]) params, covariance = curve_fit( decay_model, x, y, p0=initial_guess, bounds=bounds, )
p0 supplies starting values in model-parameter order. The lower bounds allow zero, while the upper bounds cap amplitude and decay at 5.0 and baseline at 2.0.
standard_errors = np.sqrt(np.diag(covariance)) residuals = y - decay_model(x, *params) rmse = np.sqrt(np.mean(residuals**2)) condition_number = np.linalg.cond(covariance)
print( f"parameters: amplitude={params[0]:.3f}, " f"decay={params[1]:.3f}, baseline={params[2]:.3f}" ) print( f"standard_errors: amplitude={standard_errors[0]:.3f}, " f"decay={standard_errors[1]:.3f}, baseline={standard_errors[2]:.3f}" ) print(f"rmse: {rmse:.3f}") print(f"covariance_condition: {condition_number:.1f}")
$ python fit_curve.py parameters: amplitude=2.534, decay=1.174, baseline=0.371 standard_errors: amplitude=0.009, decay=0.011, baseline=0.006 rmse: 0.007 covariance_condition: 19.9
The parameter order matches the model signature after x. RMSE has no universal pass threshold; 0.007 is small against measurements from 0.39 to 2.91. A much larger covariance condition number can signal redundant parameters or poorly scaled estimates.