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 4 of 5

Practice setLesson 3.5.4 in Level 3, Linear algebra. About 25 minutes.

Practice set: spaces and projections

Span, independence and basis

Span, independence and basis (lesson 3.5.1)

0 of 8 correct

  1. 1.
  2. 2.
  3. 3.
  4. 4.
  5. 5.
  6. 6.
  7. 7.
  8. 8.
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.

Orthogonal projection

Orthogonal projection (lesson 3.5.2)

0 of 8 correct

  1. 1.
  2. 2.
  3. 3.
  4. 4.
  5. 5.
  6. 6.
  7. 7.
  8. 8.
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.

Least squares

Least squares (lesson 3.5.3)

0 of 8 correct

  1. 1.
  2. 2.
  3. 3.
  4. 4.
  5. 5.
  6. 6.
  7. 7.
  8. 8.
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.

Coding exercise

Exercise

A projection, and a line that survives awkward data

Write two functions, with NumPy.

  1. project(A, b) returns (p, r): the projection of b onto the column space of A and the residual b - p, as in lesson 3.5.2. A 1-D A is a single column. Raise ValueError if A and b have different numbers of rows, or if the columns of A are dependent.
  2. fit_line(x, y) returns (c, m, ss): the intercept, the slope and the sum of squared residuals of the least-squares line y=c+mxy = c + mx. Use the centred form of lesson 3.5.3, m=x~⋅y~x~⋅x~m = \frac{\tilde{\mathbf{x}} \cdot \tilde{\mathbf{y}}}{\tilde{\mathbf{x}} \cdot \tilde{\mathbf{x}}} and c=yˉ−mxˉc = \bar{y} - m\bar{x}. Raise ValueError if the lengths differ, there are fewer than two points, or every xx is the same.

The tests use the examples of lessons 3.5.2 and 3.5.3 and the bad inputs above. One test has xx values near 10810^8, such as times counted in seconds. There the normal equations, and even lstsq, return nonsense, while the centred form gets the slope exactly.

See the tests
import numpy as np

X5 = [0, 1, 2, 3, 4]
Y5 = [1, 2, 4, 4, 6]
PLANE = [[1, 0], [0, 1], [1, 1]]


def _raises_value_error(fn, *args):
    try:
        fn(*args)
    except ValueError:
        return True
    return False


def test_project_onto_a_line():
    """(1, 2) projected onto the line through (3, 1) is (1.5, 0.5), with residual (−0.5, 1.5)"""
    p, r = project([3, 1], [1, 2])
    expect_close(p, [1.5, 0.5], what="projection onto the line")
    expect_close(r, [-0.5, 1.5], what="residual")


def test_project_onto_a_plane():
    """(0, 1, 4) projected onto the plane of (1, 0, 1) and (0, 1, 1) is (1, 2, 3), with residual (−1, −1, 1)"""
    p, r = project(PLANE, [0, 1, 4])
    expect_close(p, [1, 2, 3], what="projection onto the plane")
    expect_close(r, [-1, -1, 1], what="residual")


def test_residual_is_orthogonal():
    """For a random 5 × 3 matrix, the residual is orthogonal to every column"""
    rng = np.random.default_rng(3)
    A = rng.normal(size=(5, 3))
    b = rng.normal(size=5)
    p, r = project(A, b)
    expect_close(A.T @ r, [0, 0, 0], abs_tol=1e-9, what="A.T @ r")
    expect_close(p + r, b, what="p + r")


def test_point_in_the_plane():
    """(2, 3, 5) is in the plane already: it is its own projection, with residual 0"""
    p, r = project(PLANE, [2, 3, 5])
    expect_close(p, [2, 3, 5], what="projection of a point in the plane")
    expect_close(r, [0, 0, 0], abs_tol=1e-9, what="residual")


def test_dependent_columns():
    """Columns (1, 2, 3) and (2, 4, 6) are dependent: ValueError"""
    if not _raises_value_error(project, [[1, 2], [2, 4], [3, 6]], [1, 0, 0]):
        raise AssertionError("expected ValueError for dependent columns")


def test_rows_differ():
    """A with 3 rows and b with 2 entries: ValueError"""
    if not _raises_value_error(project, PLANE, [1, 2]):
        raise AssertionError("expected ValueError when A and b have different numbers of rows")


def test_fit_five_points():
    """The five points of lesson 3.5.3 give y = 1 + 1.2x with a sum of squares of 0.8"""
    c, m, ss = fit_line(X5, Y5)
    expect_close([c, m, ss], [1.0, 1.2, 0.8], what="(c, m, ss)")


def test_fit_two_points():
    """Two points (1, 3) and (3, 7) give the line through them, y = 1 + 2x, with nothing left over"""
    c, m, ss = fit_line([1, 3], [3, 7])
    expect_close([c, m], [1.0, 2.0], what="(c, m)")
    expect_close(ss, 0.0, abs_tol=1e-12, what="sum of squares")


def test_noisy_line():
    """Lesson 3.5.3's 20 noisy points (seed 353) give c = 2.4162, m = 0.4571 and a sum of squares of 24.0997"""
    rng = np.random.default_rng(353)
    x = np.arange(1, 21, dtype=float)
    y = 2 + 0.5 * x + rng.normal(0, 1, 20)
    c, m, ss = fit_line(x, y)
    A = np.column_stack([np.ones(20), x])
    expected, residual, _, _ = np.linalg.lstsq(A, y, rcond=None)
    expect_close([c, m], expected, what="(c, m)")
    expect_close(ss, residual[0], what="sum of squares")


def test_large_x():
    """x near 10⁸ with y = 2 + 0.5(x − 10⁸): the slope is exactly 0.5 and the intercept 2 − 0.5 × 10⁸"""
    x = 1e8 + np.arange(5.0)
    y = 2 + 0.5 * np.arange(5.0)
    c, m, ss = fit_line(x, y)
    expect_close(m, 0.5, rel=1e-12, what="slope")
    expect_close(c, 2 - 0.5e8, rel=1e-12, what="intercept")


def test_all_x_equal():
    """Points (2, 1), (2, 3), (2, 5) all have x = 2: ValueError"""
    if not _raises_value_error(fit_line, [2, 2, 2], [1, 3, 5]):
        raise AssertionError("expected ValueError when every x is the same")


def test_lengths_differ():
    """Five x values and four y values: ValueError"""
    if not _raises_value_error(fit_line, X5, Y5[:4]):
        raise AssertionError("expected ValueError when the lengths differ")


def test_one_point():
    """A single point does not fix a line: ValueError"""
    if not _raises_value_error(fit_line, [1], [2]):
        raise AssertionError("expected ValueError for one point")

Free further reading

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