Interactive Learning

Welcome to Math with Python

Math with Python turns Python into a math notebook: every lesson runs in the browser via Pyodide, so you plot functions, solve systems, differentiate expressions, and fit regressions without installing anything locally. The course builds NumPy and Pandas fluency alongside classic algebra-through-calculus topics, so the code and the math reinforce each other.

Each lesson mixes short explanations with runnable cells: change a coefficient, re-run, and see the plot shift. Follow the numbered lessons in the sidebar at your own pace, take the Practice Exam when you're ready, and use Exam Remediation to focus on what still needs work.

⬡ Python as a Math Notebook ⬡ NumPy for Math ⬡ Pandas for Math ⬡ Plotting Functions ⬡ Systems of Linear Equations ⬡ Symbolic Algebra with SymPy ⬡ Coordinate Geometry ⬡ Trigonometry & the Unit Circle ⬡ Function Transformations ⬡ Sequences, Limits, Series ⬡ Linear Regression

Are you ready for Math with Python?

Five quick questions. This is not graded and does not gate the course — if any of them feel hard, you'll get more out of Algebra I first.

  1. 1. Solve $2x + 5 = 17$ for $x$.

  2. 2. What is the slope of the line through $(1, 2)$ and $(4, 8)$?

  3. 3. Which point lies on $y = x^2 - 3$?

  4. 4. $\sin(\pi/2) = ?$

  5. 5. Solve $x^2 - 5x + 6 = 0$.

Answer the questions above to see if you're ready.

Python as a math notebook

Assign a value to a name, reuse the name later, and let Python remember the arithmetic. First runnable code — compound interest in six lines.

What you'll learn (best in order)
1. Variables and assignment — name = value
2. Printing results with print()
3. Arithmetic operators (+, -, *, /, **)
4. Compound interest: final = principal * (1 + rate) ** years
Big idea
  • name = value stores it; every later line that mentions the name uses the current value
  • Exponents use **, not ^
  • Write the formula once, change one input, re-run — that's the notebook workflow

Python is a calculator that remembers. You assign a value to a name with =, and every later line that mentions the name gets the current value. That's what makes it a math notebook instead of a throwaway calculation.

The Run cell below computes compound interest: an initial deposit of $1,000 at 5% interest, compounded annually for 10 years. Press Run — this is real code running in your browser.

principal = 1000
rate = 0.05
years = 10
final = principal * (1 + rate) ** years
print("After", years, "years:", round(final, 2))
print("Interest earned:", round(final - principal, 2))

Order of operations matters: ** (exponent) binds tighter than *, so principal * (1 + rate) ** years means $\text{principal} \cdot (1 + \text{rate})^\text{years}$, not $(\text{principal} \cdot (1 + \text{rate}))^\text{years}$. If you're unsure, add parentheses.

Here's a Tinker cell — a for-loop prints the balance every year so you can watch the growth curve build up. Change the interest rate to 0.08, or the number of years to 30, and rerun to see more rows appear.

principal = 1000
rate = 0.05
years = 10

# Print the year-by-year growth. Try changing rate or years and rerun.
print("year   balance")
for t in range(years + 1):
    balance = principal * (1 + rate) ** t
    print(f"{t:>3}   ${balance:>8.2f}")

Floats surprise you sometimes. Python stores decimals in binary, so 0.1 + 0.2 doesn't come out to exactly 0.3. This isn't a bug — it's the same trade-off every scientific calculator makes. Try it:

print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)   # this is False!
print(round(0.1 + 0.2, 10) == 0.3)

Exercise. Use the compound interest formula $B(t) = \text{principal} \cdot (1 + \text{rate})^t$ to compute the balance after 20 years of a $2,000 principal at a 6% annual rate. Assign the result to balance. This is the same formula the Explore below visualizes — you're computing one point on that curve.

principal = 2000
rate = 0.06
years = 20
# Apply B(t) = principal * (1 + rate) ** t and assign to `balance`.
balance = ...

Bridging Pre-Algebra — fractions and ratios. Python's built-in fractions.Fraction handles exact rational arithmetic with no floating-point drift, and percent math is just multiplication. If you drilled fractions and ratios in Pre-Algebra, here's what those calculations look like in code.

from fractions import Fraction

# Exact fraction arithmetic — no floating-point drift.
a = Fraction(1, 3)
b = Fraction(2, 5)
print("1/3 + 2/5 =", a + b)           # 11/15 exactly
print("1/3 * 2/5 =", a * b)           # 2/15 exactly
print("as decimal:", float(a + b))    # 0.7333...

# Percent math — no special library needed, just multiplication.
sticker_price = 199.99
discount = 0.20    # 20% off
tax_rate = 0.0875  # 8.75%
final = sticker_price * (1 - discount) * (1 + tax_rate)
print("final price after 20% off + 8.75% tax:", round(final, 2))

Ratios & proportions. A recipe scales by multiplying every quantity by the same factor. Fractions keep the ratio exact through the scaling.

from fractions import Fraction

# Add 1/10 to itself ten times, then compare to 1 exactly.
# Floats drift; Fractions don't. Try both.
exact = Fraction(0)
approx = 0.0
for _ in range(10):
    exact  = exact  + Fraction(1, 10)
    approx = approx + 0.1

print("exact  after 10 additions =", exact,  "==", exact  == 1)
print("approx after 10 additions =", approx, "==", approx == 1)
print("difference:", float(exact) - approx)

Explore. Compound interest is a function of time: $B(t) = \text{principal} \cdot (1 + \text{rate})^t$. Years go in, balance comes out. Slide principal and rate and watch the curve bend.