Regression
Least-Squares Line
Fit a least-squares line by computing slope = Σ(xi−mx)(yi−my) /
Σ(xi−mx)² and intercept = my − slope×mx. Two loops: the first accumulates
sx and sy for the means; the second accumulates cov_sum and var_sum. ddof
cancels (n−1 in both numerator and denominator), so unnormalized sums suffice.
Library: scipy.stats.linregress(x, y) — RESULT = (slope, intercept) rounded.
By hand
x=[1,2,3,4,5], y=[2,4,4,4,6]. mx=3, my=4. cov_sum=8, var_sum=10. slope=8/10=0.8. intercept=4−0.8×3=1.6. Line: ŷ=0.8x+1.6.
x = [1, 2, 3, 4, 5]
y = [2, 4, 4, 4, 6]
n = len(x)
sx = 0.0
sy = 0.0
for i in range(n):
sx = sx + x[i]
sy = sy + y[i]
mx = sx / n
my = sy / n
cov_sum = 0.0
var_sum = 0.0
for i in range(n):
cov_sum = cov_sum + (x[i] - mx) * (y[i] - my)
var_sum = var_sum + (x[i] - mx) ** 2
slope = cov_sum / var_sum
intercept = my - slope * mx
print('RESULT:', (round(slope, 4), round(intercept, 4)))
x ← [1, 2, 3, 4, 5]
1x = [1, 2, 3, 4, 5]2y = [2, 4, 4, 4, 6]values this step[1, 2, 3, 4, 5]xy ← [2, 4, 4, 4, 6]
1x = [1, 2, 3, 4, 5]2y = [2, 4, 4, 4, 6]3n = len(x)values this step[2, 4, 4, 4, 6]yn ← 5
2y = [2, 4, 4, 4, 6]3n = len(x)4sx = 0.0values this step5nsx ← 0.0
3n = len(x)4sx = 0.05sy = 0.0values this step0.0sxsy ← 0.0
4sx = 0.05sy = 0.06for i in range(n):values this step0.0syi ← 0, sx ← 1.0, sy ← 2.0
pass 1 of 55sy = 0.06for i in range(n):7 sx = sx + x[i]8 sy = sy + y[i]9mx = sx / nvalues this step0i0.0 → 1.0sx0.0 → 2.0syAll 5 passes — pass 1 is the card above pass isxsy1 0 0.0 → 1.0 0.0 → 2.0 2 0 → 1 1.0 → 3.0 2.0 → 6.0 3 1 → 2 3.0 → 6.0 6.0 → 10.0 4 2 → 3 6.0 → 10.0 10.0 → 14.0 5 3 → 4 10.0 → 15.0 14.0 → 20.0 for i in range(n):
5sy = 0.06for i in range(n):7 sx = sx + x[i]mx ← 3.0
8 sy = sy + y[i]9mx = sx / n10my = sy / nvalues this step3.0mxmy ← 4.0
9mx = sx / n10my = sy / n11cov_sum = 0.0values this step4.0mycov_sum ← 0.0
10my = sy / n11cov_sum = 0.012var_sum = 0.0values this step0.0cov_sumvar_sum ← 0.0
11cov_sum = 0.012var_sum = 0.013for i in range(n):values this step0.0var_sumi ← 0, cov_sum ← 4.0, var_sum ← 4.0
pass 1 of 512var_sum = 0.013for i in range(n):14 cov_sum = cov_sum + (x[i] - mx) * (y[i] - my)15 var_sum = var_sum + (x[i] - mx) ** 216slope = cov_sum / var_sumvalues this step4 → 0i0.0 → 4.0cov_sum0.0 → 4.0var_sumAll 5 passes — pass 1 is the card above pass icov_sumvar_sum1 4 → 0 0.0 → 4.0 0.0 → 4.0 2 0 → 1 — 4.0 → 5.0 3 1 → 2 — — 4 2 → 3 — 5.0 → 6.0 5 3 → 4 4.0 → 8.0 6.0 → 10.0 for i in range(n):
12var_sum = 0.013for i in range(n):14 cov_sum = cov_sum + (x[i] - mx) * (y[i] - my)slope ← 0.8
15 var_sum = var_sum + (x[i] - mx) ** 216slope = cov_sum / var_sum17intercept = my - slope * mxvalues this step0.8slopeintercept ← 1.5999999999999996
16slope = cov_sum / var_sum17intercept = my - slope * mx18print('RESULT:', (round(slope, 4), round(intercept, 4)))values this step1.5999999999999996interceptstdout ← RESULT: (0.8, 1.6)
17intercept = my - slope * mx18print('RESULT:', (round(slope, 4), round(intercept, 4)))values this stepRESULT: (0.8, 1.6)stdout
With the library
scipy.stats.linregress(x, y) returns slope, intercept, r (correlation),
pvalue, and stderr. The snapshot shows slope, intercept, and r; RESULT is
(slope, intercept).
from scipy import stats
from dalib.display import set_display
set_display()
x = [1, 2, 3, 4, 5]
y = [2, 4, 4, 4, 6]
fit = stats.linregress(x, y)
print('slope:', round(float(fit.slope), 4))
print('intercept:', round(float(fit.intercept), 4))
print('r:', round(float(fit.rvalue), 4))
print('RESULT:', (round(float(fit.slope), 4), round(float(fit.intercept), 4)))
slope: 0.8
intercept: 1.6
r: 0.8944
RESULT: (0.8, 1.6)
Honesty
This lesson shows the computation of the least-squares line exactly, on a tiny pinned sample. The slope and intercept are arithmetically correct and reproducible, but a line fit to a handful of points is a mechanism demo, not a predictive model — it is not a statistical finding. Reading significance into the slope, or extrapolating the line beyond the observed x-range, is not warranted at this sample size; real inference needs an adequate sample and assumption checks (linearity, independent errors of roughly constant variance).
Implementation notes
- ddof cancellation: slope = cov_sum/var_sum where both are unnormalized (Σ products, not divided by n−1). The n−1 factors cancel, so the result equals np.cov(x,y)[0,1]/np.var(x,ddof=1) exactly.
scipy.stats.linregressuses a numerically stable internal algorithm; parity holds to 4 dp here since the data are small integers.- The r value from linregress equals Pearson r from ch04 for the same data.
- Cross-reference:
covariance-by-hand(ch04) for the cov_sum accumulation;pearson-correlation(ch04) for the r value linregress also reports.