Skip to content

Chapter key points

Chapter 0: Start here

0.1 How this course works

  • Four Parts, sixteen chapters; episode 2.3 is the third episode of Chapter 2. Chapters 0–2 are available now; the rest are marked soon.
  • Read the Overview card first and the Key points card last; they are the contract for the page.
  • Predict boxes and checkpoints work only if you commit to an answer before looking.
  • Exercises are graded ★ (check), ★★ (apply), ★★★ (stretch). Do all the ★ ones, and at least one ★★ per episode.
  • The site keeps no record of what you have read. Only your theme choice is remembered, in this browser.

0.2 Setting up Python

  • Colab needs no install and is enough for all of Part I; a local environment is faster, private and works offline.
  • One project, one environment. Never install course libraries into the Python that came with your operating system.
  • Pinned versions are what the course was tested with; a slightly different version usually works, and check_env.py tells you when it does not.
  • The Python that runs a notebook is the kernel you selected, not the one on your command line; most ‘it works in the terminal but not in the notebook’ problems are a kernel mismatch.
  • When cells behave strangely, restart the kernel and run from the top before you look for a bug.

0.3 Python self-check

  • Python indexes from 0 and slices are half-open: xs[1:4] is items 1, 2 and 3, and range(n) stops at n − 1.
  • A function without return returns None; assigning a list to a second name does not copy it.
  • Read tracebacks from the last line upwards: the error first, then the nearest line of your own code.
  • Floats are binary approximations: never compare them with ==; use a tolerance such as math.isclose.
  • / always gives a float, // gives floor division and % the remainder; mixing them up silently changes results.
  • Seven or more out of ten right: start Chapter 1. Fewer: spend a week with one of the free refreshers and come back.

Chapter 1: Scientific Python for data work

1.1 Python refresher for scientists

  • A list keeps things in order and you look them up by position; a dictionary looks things up by name. Atomic masses are a dictionary, a series of readings is a list.
  • Indexing starts at 0 and slices stop before the end index, so items[2:5] holds exactly three items.
  • A comprehension, [f(x) for x in items], builds a new list in one readable line; use it instead of a loop that only appends.
  • Every function you write gets a docstring and sensible defaults; future you is a user too.
  • f-strings with format specs ({x:.2f}, {name:>8}) are the fastest way to readable output.
  • pathlib.Path builds file paths that work on every operating system; path.exists() saves you from confusing errors later.
  • Read a traceback from the bottom: the last line names the error, the line above it points at your code.

1.2 NumPy arrays

  • An array holds one data type in a fixed shape; that is what makes whole-array arithmetic fast and lets shape tell you what you are holding.
  • a[i, j] picks an element, a[:, j] a column, a[i] a row; slices are views, so writing to a slice changes the original.
  • A boolean mask a[a > 0] selects elements; combine conditions with & and | in parentheses, never and/or.
  • Vectorised code is tens to hundreds of times faster than the equivalent loop, and usually shorter and clearer.
  • Broadcasting stretches the smaller array along missing or length-1 axes; X - X.mean(axis=0) centres every column.
  • axis=0 collapses rows (one result per column); axis=1 collapses columns (one result per row).
  • Create one rng = np.random.default_rng(42) and draw everything from it; a fixed seed makes results reproducible.
  • Features live in X, shape (n_samples, n_features); targets in y, shape (n_samples,). Every estimator in the course expects this.

1.3 pandas DataFrames

  • A DataFrame is a table of labelled columns, each a Series with one dtype; the row labels are the index.
  • loc selects by label and iloc by position; a boolean mask inside df[...] or df.loc[...] filters rows.
  • Build conditions with &, | and parentheses; .isin() replaces a chain of == tests.
  • groupby(keys)[column].agg(...) is split-apply-combine: one row of results per group.
  • merge joins tables on a shared key; check the row count before and after, because a bad key silently multiplies rows.
  • melt turns wide tables long, pivot_table turns long tables wide; the long form is what plotting and grouping want.
  • isna().sum() is the first thing to run on new data; drop missing rows only when you know why they are missing and how many you lose.
  • Tidy data: one row per observation, one column per variable, one table per kind of thing.

1.4 Visualising data with matplotlib and seaborn

  • Plot first, summarise second: a histogram or scatter plot shows shape, outliers and gaps that a mean and a standard deviation hide.
  • Always create figures with fig, ax = plt.subplots() and draw on ax. The stateful plt.plot interface is for one-off sketches, not for work you will reuse.
  • One variable: histogram (with a KDE if the sample is large enough). One variable per group: box plot or violin. Two variables: scatter. Many variables: pairplot and a correlation heatmap.
  • seaborn’s axes-level functions take ax=; figure-level functions such as pairplot build their own figure, so call them alone in a cell.
  • Label every axis with its unit, give every figure a title or caption, and use a colour-blind-safe palette. Never encode a contrast as red against green.
  • fig.savefig("name.png", dpi=150) writes exactly the figure you see; bbox_inches="tight" stops labels being cut off.

1.5 EDA case study: aqueous solubility

  • Exploratory data analysis is the same short checklist every time: shape, types, missing values, the target’s distribution, each feature’s distribution, feature–target relationships, correlations, outliers, duplicates.
  • logS is log10 of solubility in mol/L: one unit is a factor of ten. Modelling the log turns a quantity spanning thirteen orders of magnitude into one a line can fit, and makes errors relative rather than absolute.
  • In ESOL, molecular weight (r ≈ −0.64) and ring count (r ≈ −0.51) are the descriptors most strongly related to solubility; hydrogen-bond donors and polar surface area help weakly.
  • The extremes make chemical sense: tiny polar molecules at the top, large chlorinated or polyaromatic hydrocarbons at the bottom. Outliers that make sense stay in.
  • ESOL contains no duplicated SMILES strings or compound names, but two different strings can describe one molecule; a chemistry toolkit (Chapter 5) is needed to be sure.
  • A SMILES string is a molecule written as text. Every descriptor in this file was computed from it, and so will yours be.

Chapter 2: Machine learning fundamentals

2.1 What is machine learning?

  • Supervised learning fits a function f(x; θ) from examples that come with answers: regression predicts a number, classification a category. Unsupervised learning looks for structure without labels.
  • Features go in X (one row per sample, one column per feature); labels go in y. Almost every tool in this course expects that layout.
  • Training means choosing the parameters θ that make a loss function small on the training data; for regression the loss is usually the mean squared error.
  • The goal is generalisation, performance on samples the model has never seen. A curve that passes through every training point can be a worse model than one that misses them all slightly.
  • Machine learning pays off with large, pattern-rich data and expensive-to-measure targets. It fails on tiny datasets, under distribution shift, with noisy labels and with confounded data.

2.2 Linear regression from scratch, then scikit-learn

  • A linear model predicts ŷ = Xθ; fitting means choosing θ to minimise the mean squared error on the training data.
  • The normal equation gives the exact least-squares solution in one line: solve (X⊤X)θ = X⊤y with np.linalg.solve, never with an explicit inverse.
  • Gradient descent reaches the same answer step by step: compute the gradient, move against it, repeat. The learning rate decides whether it converges, crawls or explodes.
  • Standardise features before gradient descent. The normal equation does not care about scale; gradient descent does.
  • scikit-learn wraps all of this in the estimator API: fit, predict, score, coef_, intercept_. Every model in this course uses the same names.
  • Coefficients are not effects. They depend on the units of the feature and on which other, correlated features are in the model.
  • Everything in this episode is fitted and scored on the same data. Whether the model is any good is the question of episode 2.3.

2.3 Evaluating models honestly

  • Performance on training data is not evidence. Always report metrics on data the model has never seen.
  • Split once, reproducibly, with train_test_split(..., random_state=42), and keep the test set untouched until the end.
  • A model that cannot beat a DummyRegressor (or DummyClassifier) has learned nothing useful.
  • MAE is the typical error in target units; RMSE punishes large errors more; R² says how much variance is explained. Report at least two of them.
  • Metrics summarise, plots explain. A parity plot and a residual plot show where a model goes wrong.

2.4 Overfitting, regularisation and cross-validation

  • For nested least-squares models, the best training error cannot increase with flexibility; test error can rise as the model starts fitting noise.
  • Bias is error from being too rigid; variance is error from following noise. Every model choice trades one for the other.
  • A learning curve says whether more data would help; a validation curve says how a hyperparameter should be set.
  • Ridge shrinks coefficients smoothly; lasso sets some exactly to zero. Both need scaled features, so put a StandardScaler in the pipeline.
  • Cross-validation replaces one lucky or unlucky split with k systematic ones; report the mean and the spread.
  • Choose hyperparameters with cross-validation on the training data; the test set is touched once, at the very end, and never used to choose anything.

2.5 Classification

  • Logistic regression is a linear model whose output passes through the sigmoid to give a probability; the class is a threshold on that probability.
  • Always compare against DummyClassifier; on imbalanced data a useless model can have impressive accuracy.
  • The confusion matrix is the ground truth. Precision, recall and F1 are read from it; report them for the class you care about.
  • ROC/AUC measures ranking across all thresholds; the precision–recall curve is the honest picture when positives are rare.
  • The threshold is a decision, not a constant: move it to trade recall against precision.

2.6 Pipelines, preprocessing and data leakage

  • Scale features for kNN, logistic regression, ridge and lasso; fit the scaler on the training data only.
  • A Pipeline bundles preprocessing and model into one estimator: fit learns everything from the training set, predict applies it unchanged to new data.
  • ColumnTransformer sends numeric columns through imputation and scaling and categorical columns through one-hot encoding; get_feature_names_out() tells you what came out.
  • Leakage is any way information about the test set reaches the model before evaluation. The test score then measures memory, not generalisation.
  • Duplicates, repeated patients or batches, preprocessing fitted on all data and after-the-fact columns are the four leaks to check for in every scientific dataset.
  • GroupKFold keeps every sample of a group on one side of the split; scaffold and sequence-identity splits are the same idea for molecules and proteins.
  • Save the whole pipeline with joblib.dump, fix every seed, pin versions and never touch the test set twice.