essay
Least Squares: Fitting a Straight Line with Python
Derive the straight-line fitting formula starting from the sum of squared residuals, and use a runnable Python example to plot the observed points, the fitted line, and the residuals.
Cite this article
APA
Zhang, C. (2026, November 13). Least Squares: Fitting a Straight Line with Python. Chester Zhang. https://chesterzhang.site/writing/least-squares
GB/T 7714
Chester Zhang. Least Squares: Fitting a Straight Line with Python[EB/OL]. (2026-11-13)[2026-11-13]. https://chesterzhang.site/writing/least-squares.
BibTeX
@online{zhang2026least,
author = {Zhang, Chester},
title = {Least Squares: Fitting a Straight Line with Python},
year = {2026},
month = nov,
date = {2026-11-13},
organization = {Chester Zhang},
url = {https://chesterzhang.site/writing/least-squares},
urldate = {2026-11-13},
}Given a set of points, how do we find the straight line that best represents them? Least squares turns this problem into a computable objective. We derive the formula using five observed points, then use Python to compute the line and plot the residuals.
Writing "best fit" as an objective
Let the -th observed point be , and let the fitted line be . At the same , the vertical difference between the observed value and the fitted value is called the residual.
Least squares chooses the slope and intercept that minimize . After squaring, positive and negative residuals do not cancel out. Larger residuals are also penalized more heavily, so outliers can noticeably pull the fitted line.
Why the fitted line passes through the sample center
Let and be the sample means. Split the error into a centered part and a constant part, then use the fact that the sum of the centered variables is zero to eliminate the cross term.
When is fixed, the first term is unchanged, and the second term reaches zero at . Therefore the fitted line must pass through . Substituting this intercept back in, taking the derivative with respect to and setting the derivative to zero gives
As long as the are not all the same, the denominator is positive, and what remains is an upward-opening quadratic, so we get a unique global optimum. If all the are the same, the data can only determine the best predicted value at that x-coordinate, , and the slope and intercept cannot be determined separately.
Fitting with Python and plotting the residuals
Click the run button on the code block. The scatter points are the observed values, the solid line is the fitted line, and the dashed segments mark the vertical residuals of each point. The code directly computes the formula above and prints the sum of squared residuals.
import matplotlib.pyplot as plt
x = [0, 1, 2, 3, 4]
y = [1.0, 2.1, 2.7, 4.2, 5.1]
n = len(x)
x_mean = sum(x) / n
y_mean = sum(y) / n
sxx = sum((xi - x_mean) ** 2 for xi in x)
if sxx == 0:
raise ValueError("At least two distinct x values are required.")
sxy = sum((xi - x_mean) * (yi - y_mean) for xi, yi in zip(x, y))
slope = sxy / sxx
intercept = y_mean - slope * x_mean
fitted = [slope * xi + intercept for xi in x]
residuals = [yi - fit for yi, fit in zip(y, fitted)]
sse = sum(residual ** 2 for residual in residuals)
print(f"Slope: {slope:.3f}")
print(f"Intercept: {intercept:.3f}")
print(f"Sum of squared residuals: {sse:.4f}")
fig, ax = plt.subplots(figsize=(6.4, 3.8), layout="constrained")
ax.plot(x, fitted, color="#1d4ed8", label="Least-squares line")
ax.vlines(x, fitted, y, colors="#b45309", linestyles="--",
linewidth=2, label="Residuals")
ax.scatter(x, y, color="#0f172a", zorder=3, label="Observations")
ax.set(xlabel="x", ylabel="y", title="Least-squares fit")
ax.grid(alpha=0.18)
ax.legend(loc="upper left")For this dataset we get , and the sum of squared residuals is . You can change the last observed value from to and run it again to see how the point on the right pulls the slope up. Goodness of fit only describes how well the line fits this dataset. When using it to predict values outside the observed range, you also need to judge whether there is reason for the linear relationship to continue to hold.