Difference quotients from scratch with Python

fastai
Published

October 19, 2020

Difference quotients from scratch with Python

From the Data Science from Scratch book.

Libraries and helper functions

import pandas as pd
import altair as alt
from typing import List
Vector = List[float]
Vector
typing.List[float]
def dot(vector1: Vector, vector2: Vector) -> float:
    assert len(vector1) == len(vector2)

    return sum(v1 * v2 for v1, v2 in zip(vector1, vector2))


assert dot([1, 2, 3], [4, 5, 6]) == 32
def sum_of_squares(v: Vector) -> Vector:
    return dot(v, v)

assert sum_of_squares([1, 2, 3]) == 14

Difference quotient

from typing import Callable

def difference_quotient(
    f: Callable[[float], float],
    x: float,
    h: float
) -> float :
    return (f(x + h) - f(x)) / h

A simple estimate

def square(x: float) -> float:
    return x * x
def derivative_x2(x: float) -> float:
    return 2 * x
xs = range(-10, 11)
actuals = [derivative_x2(x) for x in xs]
actuals
[-20,
 -18,
 -16,
 -14,
 -12,
 -10,
 -8,
 -6,
 -4,
 -2,
 0,
 2,
 4,
 6,
 8,
 10,
 12,
 14,
 16,
 18,
 20]
estimates = [difference_quotient(square, x, h=0.001) for x in xs]
estimates
[-19.998999999984335,
 -17.998999999988996,
 -15.999000000007868,
 -13.999000000005424,
 -11.99900000000298,
 -9.999000000004088,
 -7.998999999999867,
 -5.998999999999199,
 -3.9989999999994197,
 -1.998999999999973,
 0.001,
 2.0009999999996975,
 4.000999999999699,
 6.000999999999479,
 8.0010000000037,
 10.001000000002591,
 12.001000000005035,
 14.00100000000748,
 16.000999999988608,
 18.000999999983947,
 20.000999999993496]
df = pd.DataFrame({'actuals': actuals, 'estimates': estimates}).reset_index()
df = df.melt(id_vars='index')
df.sample(10)
index variable value
5 5 actuals -10.000
13 13 actuals 6.000
34 13 estimates 6.001
2 2 actuals -16.000
19 19 actuals 18.000
0 0 actuals -20.000
7 7 actuals -6.000
3 3 actuals -14.000
24 3 estimates -13.999
11 11 actuals 2.000
alt.Chart(df).mark_circle(opacity=0.75).encode(
    alt.X('index:Q'),
    alt.Y('value:Q'),
    alt.Size('variable:N'),
    alt.Color('variable:N')
).properties(title='Actual derivatives and estimated quotients')

Calculating an i-th difference quotient

def partial_difference_quotient(
    f: Callable[[Vector], float],
    v: Vector,
    i: int,
    h: float
) -> float:
    """Return i-th parital difference quotient of `f` at a`v`"""
    w = [
        v_j + (h if j == i else 0)
        for j, v_j in enumerate(v)
    ]

    return (f(w) - f(v)) / h
def estimate_gradient(
    f: Callable[[Vector], float],
    v: Vector,
    h: float = 0.0001
):
    return [
        partial_difference_quotient(f, v, i, h)
        for i in range(len(v))
    ]