Slicing, comprehensions, dicts, first NumPy — what the notebooks assume
Do this alongside Course 1, before the first lab. Goal: slicing, comprehensions and your first NumPy — the three things course notebooks assume without ever explaining.
You know takes[0]. Slicing grabs a range:
takes = [12.5, 8.0, 22.1, 15.3, 9.9, 30.0]
print(takes[1:4]) # [8.0, 22.1, 15.3] — from index 1 UP TO BUT NOT INCLUDING 4
print(takes[:3]) # [12.5, 8.0, 22.1] — empty start = from the beginning
print(takes[3:]) # [15.3, 9.9, 30.0] — empty end = to the end
print(takes[-2:]) # [9.9, 30.0] — last two
The rule that prevents every confusion: start is included, end is not. [1:4] gives you indexes 1, 2, 3. That's also why [:3] + [3:] = the whole list with nothing doubled — which is exactly how train/test splits work.
In editing terms: a slice is an in/out point. The in point is on the cut; the out point is after the last frame you keep.
x = [10, 20, 30, 40, 50] — predict x[1:3], x[:2], x[-1:].x[:2] + x[2:] reassemble the list perfectly?You will meet this constantly and it looks like magic until someone shows you the unfolding:
# the one-liner…
doubled = [t * 2 for t in takes]
# …is EXACTLY this loop:
doubled = []
for t in takes:
doubled.append(t * 2)
Read a comprehension inside-out: "for each t in takes, keep t * 2."
With a filter:
long_takes = [t for t in takes if t > 10]
# for each t in takes, keep t — but only if t > 10
That's it. Any comprehension you ever see can be unfolded into a for (+ optional if) exactly like this. When one confuses you, unfold it.
Unfold this into a normal loop: names = [f.upper() for f in files if f.endswith(".mov")]
clip = "A014C022"
dur = 12.5
print(f"clip {clip} runs {dur} seconds")
# clip A014C022 runs 12.5 seconds
The f before the quote makes {...} slots live. {dur:.2f} formats to 2 decimals — you'll see f"loss: {loss:.4f}" in every training loop in this certificate.
Lists index by position; dicts index by name:
clip = {"name": "A014C022", "fps": 29.97, "frames": 375}
print(clip["fps"]) # 29.97
clip["codec"] = "ProRes" # add a new key
Where you'll meet them: every model config, every API response, every {"prompt": ..., "chosen": ..., "rejected": ...} training example in course 11. A dict is a labelled bin; the label is the key, the contents the value.
for key, value in clip.items():
print(key, "→", value)
params = {"lr": 0.001, "epochs": 10} — how do you read the learning rate?params["batch"]? (Run it. Read the error. Errors are information.)Python lists can't do arithmetic on all elements at once. NumPy arrays can — and that single idea is the foundation of everything from Course 1 to Course 13.
import numpy as np
x = np.array([12.5, 8.0, 22.1, 15.3])
print(x * 2) # [25. 16. 44.2 30.6] — EVERY element, no loop
print(x.mean()) # 14.475
print(x.max()) # 22.1
print(x > 10) # [ True False True True]
print(x[x > 10]) # [12.5 22.1 15.3] — filter by a condition!
x * 2 on a list would error (well — [list] * 2 duplicates it, worse). On an array it multiplies every element. This "do it to everything at once" is called vectorisation, and it's why ML code has so few loops.
X = np.array([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]])
print(X.shape) # (2, 3) ← 2 rows, 3 columns
print(X[0]) # [1. 2. 3.] — row 0
print(X[0, 2]) # 3.0 — row 0, column 2
print(X[:, 0]) # [1. 4.] — ALL rows, column 0
X[:, 0] is the one to burn in: the : means "everything" in that position. All rows, column 0. In this certificate, rows are samples and columns are features — so X[:, 0] is "the first feature of every sample" and X[0] is "everything about the first sample".
X.shape answers the question you should ask about every array you ever meet: what shape is this? When course code breaks, the answer is almost always a shape.
X.shape?X[:, 2] mean in dataset language? And X[10]?np.array([1,2,3]) + np.array([10,20,30]).Course 1's split step, now fully decodable:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
a, b = 1, 2.test_size=0.2 — keyword argument: hold out 20%.X was (150, 4), then X_train is (120, 4) and X_test is (30, 4). Check with .shape — always check with .shape.[a:b]: a included, b excluded; [:k] and [k:] split cleanly — that IS train/testf"{value:.4f}" — values inside strings; you'll read this in every loss printoutx[x > 10] filters; .shape is the first questionX[:, j] = feature j of all samples; X[i] = all features of sample ia, b, c, d = f(...) — one call, several results, caught by position