“We fitted a Gaussian” is half of most results sections. scipy.optimize.curve_fit does the fit; the part people skip — and reviewers ask about — is the uncertainties, which come from the same call. This lesson does both.
Step 1 — Define the model
The model is a plain function: first argument x, then the parameters to fit:
import numpy as np
def gaussian(x, a, mu, sigma):
return a * np.exp(-((x - mu) ** 2) / (2 * sigma ** 2))
Step 2 — Make some honest test data
While building the figure, synthetic data with known parameters tells you whether the fit machinery works — if it can’t recover mu = 5, it won’t recover your real peak either:
rng = np.random.default_rng(1)
x = np.linspace(0, 10, 80)
y = gaussian(x, a=1.0, mu=5.0, sigma=0.5) + rng.normal(0, 0.05, x.size)
Step 3 — Fit
from scipy.optimize import curve_fit
p0 = [1, 4, 1] # rough initial guesses: a, mu, sigma
popt, pcov = curve_fit(gaussian, x, y, p0=p0)
perr = np.sqrt(np.diag(pcov)) # 1-sigma uncertainties
for name, val, err in zip(["a", "mu", "sigma"], popt, perr):
print(f"{name} = {val:.3f} ± {err:.3f}")
popt is the best-fit parameters; the diagonal of the covariance matrix gives their standard errors. The printed value ± error lines are exactly what goes in the paper.
Step 4 — Plot data and fit together
import matplotlib.pyplot as plt
xs = np.linspace(x.min(), x.max(), 400)
plt.scatter(x, y, s=10, label="data")
plt.plot(xs, gaussian(xs, *popt), linewidth=2, label="fit")
plt.xlabel("x")
plt.ylabel("signal")
plt.legend()
Evaluate the fitted curve on a fine grid (xs), not on the data points — a smooth line over discrete markers is the convention readers expect.
Step 5 — Export or insert
Export / Insert for the vector PDF or a placed figure with \label. Because the script is the saved source, the quoted parameters and the drawn curve can never drift apart — both come from the same popt.
Tip: If
curve_fitreturns nonsense, the initial guessp0is almost always the culprit. Plot the model atp0over the data once — if the starting curve is nowhere near, the optimizer has nothing to climb.