SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 3.5.1 Span, independence and basisConcept · 35 min
      2. 3.5.2 Orthogonal projectionConcept · 35 min
      3. 3.5.3 Least squaresConcept · 30 min
      4. 3.5.4 Practice set: spaces and projectionsPractice set · 25 min
      5. 3.5.5 Case study: the minimum-variance hedge as a projectionCase study · 35 min

Level 3, Linear algebra 3.5 Spaces and projections Lesson 3 of 5

ConceptLesson 3.5.3 in Level 3, Linear algebra. About 30 minutes.

Least squares

A system with no solution

Lesson 3.3.2 solved a tall system, three equations in two unknowns. Change its last right-hand side from 4 to 5:

x1+x2=3x1−x2=1x1+2x2=5\begin{aligned} x_1 + x_2 &= 3 \\ x_1 - x_2 &= 1 \\ x_1 + 2x_2 &= 5 \end{aligned}

The first two equations give (2,1)(2, 1), and then the third reads 4=54 = 5. As a matrix equation this is Tx=bT\mathbf{x} = \mathbf{b} with b=(3,1,5)\mathbf{b} = (3, 1, 5). Lesson 3.5.1 found that b\mathbf{b} is not in the column space of TT, so no x\mathbf{x} solves it.

A system with more equations than unknowns is overdetermined. Data gives such systems all the time: many measurements, few unknowns, and a little error in each measurement.

The best answer

When no x\mathbf{x} makes Tx=bT\mathbf{x} = \mathbf{b}, look for the x\mathbf{x} that makes TxT\mathbf{x} as close to b\mathbf{b} as possible. The gap Tx−bT\mathbf{x} - \mathbf{b} has one entry per equation, the amount by which that equation misses. Its squared length is the sum of the squared misses.

x\mathbf{x}TxT\mathbf{x}Tx−bT\mathbf{x} - \mathbf{b}∥Tx−b∥2\lVert T\mathbf{x} - \mathbf{b} \rVert^2
(2,1)(2, 1)(3,1,4)(3, 1, 4)(0,0,−1)(0, 0, -1)1
(2,1.5)(2, 1.5)(3.5,0.5,5)(3.5, 0.5, 5)(0.5,−0.5,0)(0.5, -0.5, 0)0.5
(2.5,1)(2.5, 1)(3.5,1.5,4.5)(3.5, 1.5, 4.5)(0.5,0.5,−0.5)(0.5, 0.5, -0.5)0.75
(2.1429,1.2857)(2.1429, 1.2857)(3.4286,0.8571,4.7143)(3.4286, 0.8571, 4.7143)(0.4286,−0.1429,−0.2857)(0.4286, -0.1429, -0.2857)0.2857

The first row meets two equations exactly and misses the third by 1. The last row meets none of them exactly, yet its total is the smallest. It is the least-squares solution, found below.

Rule: Least squares

x^ makes ∥Ax−b∥2 smallest\hat{\mathbf{x}} \text{ makes } \lVert A\mathbf{x} - \mathbf{b} \rVert^2 \text{ smallest}

x^\hat{\mathbf{x}}, read "x hat", is the least-squares solution of Ax=bA\mathbf{x} = \mathbf{b}. The squared length adds the squares of the misses. When the system has a solution, x^\hat{\mathbf{x}} is that solution, with every miss 0.

The normal equations

Every product AxA\mathbf{x} lies in the column space of AA. The point of the column space closest to b\mathbf{b} is its projection p\mathbf{p} (lesson 3.5.2). So Ax^=pA\hat{\mathbf{x}} = \mathbf{p}, and the residual b−Ax^\mathbf{b} - A\hat{\mathbf{x}} is orthogonal to every column of AA.

Rule: The normal equations

A⊤(b−Ax^)=0A⊤A x^=A⊤b\begin{gathered} A^\top(\mathbf{b} - A\hat{\mathbf{x}}) = \mathbf{0} \\[4pt] A^\top A\,\hat{\mathbf{x}} = A^\top\mathbf{b} \end{gathered}

"Normal" means at right angles. When the columns of AA are independent, A⊤AA^\top A has an inverse and x^=(A⊤A)−1A⊤b\hat{\mathbf{x}} = (A^\top A)^{-1}A^\top\mathbf{b}, exactly the projection formula of lesson 3.5.2.

For the system above:

T⊤T=(3226),T⊤b=(912)3x1+2x2=9,2x1+6x2=12\begin{gathered} T^\top T = \begin{pmatrix} 3 & 2 \\ 2 & 6 \end{pmatrix}, \quad T^\top\mathbf{b} = \begin{pmatrix} 9 \\ 12 \end{pmatrix} \\[6pt] 3x_1 + 2x_2 = 9, \quad 2x_1 + 6x_2 = 12 \end{gathered}

Three times the first equation minus the second gives 7x1=157x_1 = 15. So x^=(157,97)≈(2.1429,1.2857)\hat{\mathbf{x}} = \left(\frac{15}{7}, \frac{9}{7}\right) \approx (2.1429, 1.2857).

The residual is b−Tx^=17(−3,1,2)\mathbf{b} - T\hat{\mathbf{x}} = \frac{1}{7}(-3, 1, 2). It is orthogonal to both columns: 17(−3+1+2)=0\frac{1}{7}(-3 + 1 + 2) = 0 and 17(−3−1+4)=0\frac{1}{7}(-3 - 1 + 4) = 0. Its squared length is 1449=27≈0.2857\frac{14}{49} = \frac{2}{7} \approx 0.2857, the smallest in the table.

Try it

The cell forms the normal equations for TT, solves them, and prints the residual, T⊤rT^\top\mathbf{r} and the sum of squares.

The loop is the table above. round keeps the last digits from showing rounding.

⌘/Ctrl + Enter runs the cell

Fitting a line to points

Lesson 1.2.1 drew the line through two points. Points that do not lie on one line have no line through them all, but least squares finds the line that comes closest.

Take five points: (0,1)(0, 1), (1,2)(1, 2), (2,4)(2, 4), (3,4)(3, 4) and (4,6)(4, 6). Look for a line y=c+mxy = c + mx, with intercept cc and slope mm. Each point asks for c+mxi=yic + m x_i = y_i, so there are five equations in two unknowns:

(1011121314)(cm)=(12446)\begin{pmatrix} 1 & 0 \\ 1 & 1 \\ 1 & 2 \\ 1 & 3 \\ 1 & 4 \end{pmatrix} \begin{pmatrix} c \\ m \end{pmatrix} = \begin{pmatrix} 1 \\ 2 \\ 4 \\ 4 \\ 6 \end{pmatrix}

The column of ones carries the intercept, and the column of xx values the slope. The normal equations hold the sums of the data:

A⊤A=(5101030),A⊤y=(1746)\begin{gathered} A^\top A = \begin{pmatrix} 5 & 10 \\ 10 & 30 \end{pmatrix}, \quad A^\top\mathbf{y} = \begin{pmatrix} 17 \\ 46 \end{pmatrix} \end{gathered}

Here 5 is the number of points, 10 the sum of the xix_i and 30 the sum of their squares. On the right, 17 is the sum of the yiy_i and 46 the sum of the products xiyix_iy_i.

Solving gives c=1c = 1 and m=1.2m = 1.2: the line y=1+1.2xy = 1 + 1.2x.

xix_i01234
yiy_i12446
fitted 1+1.2xi1 + 1.2x_i12.23.44.65.8
residual0−0.2-0.20.6−0.6-0.60.2

The squared residuals add up to 0.8. No other line does better: the line through the first and last points leaves 0.875, and y=1+xy = 1 + x leaves 2.

Two lines through five points, with each residual drawn as a vertical gap. Least squares makes the squared gaps add up to the least possible, 0.8; the line through the end points leaves 0.875.

Each residual is a vertical gap, a miss in yy, not the distance from the point to the line at right angles. The right angle of lesson 3.5.2 lives in R5\mathbb{R}^5: the residual vector is orthogonal to the two columns of AA.

That right angle says two things you can check. The residuals add up to 0, since they are orthogonal to the column of ones. And the line passes through the mean point (xˉ,yˉ)=(2,3.4)(\bar{x}, \bar{y}) = (2, 3.4): 1+1.2×2=3.41 + 1.2 \times 2 = 3.4.

The slope as a projection

Centre the data as in case study 3.1.4: subtract the means to get x~\tilde{\mathbf{x}} and y~\tilde{\mathbf{y}}. The slope is the coefficient of the projection of y~\tilde{\mathbf{y}} onto x~\tilde{\mathbf{x}} (lesson 3.5.2), and the intercept puts the line through the mean point.

Rule: Least-squares line

m=x~⋅y~x~⋅x~c=yˉ−mxˉ\begin{gathered} m = \frac{\tilde{\mathbf{x}} \cdot \tilde{\mathbf{y}}}{\tilde{\mathbf{x}} \cdot \tilde{\mathbf{x}}} \\[4pt] c = \bar{y} - m\bar{x} \end{gathered}

x~=x−xˉ 1\tilde{\mathbf{x}} = \mathbf{x} - \bar{x}\,\mathbf{1} and y~=y−yˉ 1\tilde{\mathbf{y}} = \mathbf{y} - \bar{y}\,\mathbf{1} are the centred data. Here x~⋅y~=12\tilde{\mathbf{x}} \cdot \tilde{\mathbf{y}} = 12 and x~⋅x~=10\tilde{\mathbf{x}} \cdot \tilde{\mathbf{x}} = 10, so m=1.2m = 1.2, and c=3.4−1.2×2=1c = 3.4 - 1.2 \times 2 = 1.

Case study 3.5.5 uses exactly this projection to find a hedge.

Try it

The first lines build AA from a column of ones and the xx values, form the normal equations and solve them.

Then the residuals, their sum of squares, their sum and their dot product with x\mathbf{x}. The last lines are the slope from the centred data, and NumPy's own polyfit, which lists the slope first.

⌘/Ctrl + Enter runs the cell

A noisy line

Real data has more points and more noise. The next cell makes 20 points from the line y=2+0.5xy = 2 + 0.5x, adds a random error to each, and fits a line. The data is invented with a fixed seed, so every run gives the same points.

The fit is y=2.4162+0.4571xy = 2.4162 + 0.4571x: close to the line the points came from, but not on it, because of the noise. How far off a fit like this can be is a question of statistics, which module 5.10 adds.

Try it

rng.normal(0, 1, 20) draws 20 random errors of typical size 1 (Level 5 explains it).

np.linalg.lstsq returns four things: x^\hat{\mathbf{x}}, the sum of squared residuals, the rank of AA, and numbers called singular values (lesson 3.8.1). The last line solves the normal equations instead, for comparison.

⌘/Ctrl + Enter runs the cell

Solve with lstsq, not with AᵀA

The normal equations are the idea; np.linalg.lstsq(A, b, rcond=None) is the tool. It works on AA itself and never forms A⊤AA^\top A.

Forming A⊤AA^\top A squares the condition number of lesson 3.3.2, which measures how much a system can magnify small errors such as rounding. A computer keeps about 16 significant digits, and a condition number of 10k10^k can cost about kk of them.

Calendar years. Fit a trend against the years 2015 to 2024. The column of ones and the column of years point almost the same way: the angle between them is 0.08°. Then cond⁡(A)≈1.4×106\operatorname{cond}(A) \approx 1.4 \times 10^6, but cond⁡(A⊤A)≈2.0×1012\operatorname{cond}(A^\top A) \approx 2.0 \times 10^{12}, which leaves only about 4 safe digits.

Centring the years first, as for the slope above, makes the two columns orthogonal. The condition number drops to 2.8723. Centring helps lstsq too: practice set 3.5.4 meets xx values so large that it loses the slope without it.

Columns that differ in the eighth decimal. Take columns (1,10−8,0)(1, 10^{-8}, 0) and (1,0,10−8)(1, 0, 10^{-8}). Each diagonal entry of A⊤AA^\top A is 1+10−161 + 10^{-16}, which a computer rounds to exactly 1. So the computed A⊤AA^\top A is (1111)\begin{pmatrix} 1 & 1 \\ 1 & 1 \end{pmatrix}, singular, and solve stops with an error.

lstsq still finds x^=(1,1)\hat{\mathbf{x}} = (1, 1) for b=(2,10−8,10−8)\mathbf{b} = (2, 10^{-8}, 10^{-8}).

Rule: In code

np.linalg.lstsq(A, b, rcond=None)\texttt{np.linalg.lstsq(A, b, rcond=None)}

Use it for least squares rather than solving A⊤A x^=A⊤bA^\top A\,\hat{\mathbf{x}} = A^\top\mathbf{b}, which squares the condition number. rcond=None sets how small a singular value counts as zero; it is the setting NumPy recommends.

Try it

The first lines compare the condition numbers for the years, before and after centring, and print the angle between the two columns.

Then the second example: the computed A⊤AA^\top A is exactly singular, solve raises LinAlgError, and lstsq returns (1,1)(1, 1).

⌘/Ctrl + Enter runs the cell

When the columns are dependent

If the columns of AA are dependent (lesson 3.5.1), A⊤AA^\top A has no inverse. The projection p\mathbf{p} is still unique: it is the nearest point of the column space. But many x^\hat{\mathbf{x}} give that same p\mathbf{p}.

Fit the five points with a third column that is twice the second, y=c+m1x+m2(2x)y = c + m_1x + m_2(2x). Every choice with c=1c = 1 and m1+2m2=1.2m_1 + 2m_2 = 1.2 gives the fitted values (1,2.2,3.4,4.6,5.8)(1, 2.2, 3.4, 4.6, 5.8): (1,1.2,0)(1, 1.2, 0), (1,0,0.6)(1, 0, 0.6) and (1,0.24,0.48)(1, 0.24, 0.48) among them.

lstsq does not fail here. It reports rank 2 instead of 3, returns the shortest x^\hat{\mathbf{x}}, here (1,0.24,0.48)(1, 0.24, 0.48), and leaves its sum of squares empty. Read the rank before reading the answer.

The same happens when every xix_i is the same: the column of xx values is a multiple of the column of ones. For points (2,1)(2, 1), (2,3)(2, 3), (2,5)(2, 5), a best line would be vertical, which has no slope (lesson 1.2.1). lstsq reports rank 1 and returns (0.6,1.2)(0.6, 1.2), which means nothing as an intercept and slope.

Rule: Dependent columns

p is unique;x^ is not\begin{gathered} \mathbf{p} \text{ is unique;} \\ \hat{\mathbf{x}} \text{ is not} \end{gathered}

When the rank is below the number of columns, many x^\hat{\mathbf{x}} give p\mathbf{p}. Each is one of them plus a vector of the null space (lesson 3.5.1). Check the rank that lstsq returns.

Try it

D has the columns 1\mathbf{1}, x\mathbf{x} and 2x2\mathbf{x}. lstsq returns its shortest answer and rank 2; the loop shows that three different answers give the same fitted values.

The last lines are the three points with the same xx.

⌘/Ctrl + Enter runs the cell

Case study 3.5.5 uses least squares twice: for the minimum-variance hedge, and for the closest copy of a payoff that cannot be copied exactly. Module 5.10 adds the statistics: how far x^\hat{\mathbf{x}} may be from the truth.

Common mistakes

  1. Least squares counts squared misses, not equations met. (2,1.5)(2, 1.5) meets two of the three equations exactly, but its total, 0.5, is larger than the least-squares 0.2857.
  2. Residuals are vertical gaps, misses in yy. They are not the distances from the points to the line at right angles.
  3. Keep the column of ones. Without it the line must pass through the origin: y=mxy = mx gives m=4630≈1.5333m = \frac{46}{30} \approx 1.5333 and a sum of squares of 2.4667, not 0.8.
  4. The order of the columns is the order of x^\hat{\mathbf{x}}. With the ones first, the intercept comes first; np.polyfit lists the slope first.
  5. Do not form A⊤AA^\top A for real data. It squares the condition number; use lstsq, and centre data such as calendar years.
  6. Check the rank. With dependent columns lstsq still returns an answer, but not the only one.

Least squares

0 of 11 correct

  1. 1.
  2. 2.
  3. 3.
  4. 4.
  5. 5.
  6. 6.
  7. 7.
  8. 8.
  9. 9.
  10. 10.
  11. 11.
How to type answers
  • Powers with ^: x^7, 2^(1/3), a^-2.
  • Multiply with * or by writing side by side: 3x^2, 2(x+1).
  • ln(x) is the natural log, log(x, 2) is log base 2, e is Euler's number, sqrt(x) is a square root.
  • sin(x), cos(x) and tan(x) take radians; write pi for π and sin(x)^2 for sin²x.
  • Your first answer to each question is saved: it counts towards your score and brings the question back for review.
  • Exact answers like ln(20)/ln(3) are best. Decimals need about 4 significant figures.

Summary card

  • An overdetermined system Ax=bA\mathbf{x} = \mathbf{b} usually has no solution. The least-squares solution x^\hat{\mathbf{x}} makes the sum of squared misses ∥Ax−b∥2\lVert A\mathbf{x} - \mathbf{b} \rVert^2 smallest.
  • Ax^A\hat{\mathbf{x}} is the projection of b\mathbf{b} onto the column space, so the residual is orthogonal to the columns: the normal equations A⊤A x^=A⊤bA^\top A\,\hat{\mathbf{x}} = A^\top\mathbf{b}.
  • A line y=c+mxy = c + mx through points uses A=[1 x]A = [\mathbf{1}\ \mathbf{x}]. The residuals are vertical gaps, add up to 0, and the line passes through (xˉ,yˉ)(\bar{x}, \bar{y}); the slope is x~⋅y~x~⋅x~\frac{\tilde{\mathbf{x}} \cdot \tilde{\mathbf{y}}}{\tilde{\mathbf{x}} \cdot \tilde{\mathbf{x}}}.
  • Use np.linalg.lstsq(A, b, rcond=None): forming A⊤AA^\top A squares the condition number.
  • With dependent columns, p\mathbf{p} is unique but x^\hat{\mathbf{x}} is not; lstsq returns the shortest and reports the rank.

Free further reading

Optional. This lesson covers what you need; these add more examples and practice. All are free to read.