# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: 'Python 3.7.9 64-bit (''blog'': conda)'
#     metadata:
#       interpreter:
#         hash: a791f4b9424e3975ed64217961a14e58af61de2cbd0b16cd6f0b3a8e9eaf4389
#     name: 'Python 3.7.9 64-bit (''blog'': conda)'
# ---

# # Difference quotients from scratch with Python

#
#
# From the [Data Science from Scratch book](https://www.oreilly.com/library/view/data-science-from/9781492041122/).

# ## Libraries and helper functions

import pandas as pd
import altair as alt

from typing import List
Vector = List[float]
Vector


# +
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

estimates = [difference_quotient(square, x, h=0.001) for x in xs]
estimates

df = pd.DataFrame({'actuals': actuals, 'estimates': estimates}).reset_index()
df = df.melt(id_vars='index')
df.sample(10)

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))
    ]

StarcoderdataJupyterScriptsDedupFiltered