Most questions involve more than one variable, and then what matters is how they depend on each other. Conditioning, slicing the data by one variable to look at another, is the single most useful idea in statistics.
Code: data and style used in this chapter
import matplotlib.pyplot as pltimport numpy as npimport statsmodels.api as smfrom matplotlib.axes import Axesfrom scipy import statsfrom sklearn import datasetsfrom sklearn.mixture import GaussianMixturefrom statly import stylestyle.use()rng = np.random.default_rng(1)engel = ( sm.datasets.engel.load_pandas().data) # income and food spending, Belgian households (1857)iris = datasets.load_iris(as_frame=True).frame # flowers of three iris speciesSPECIES_COLORS = np.array([style.BLUE, style.GREEN, style.CORAL])
2.4.1 Joint, marginal and conditional
Two variables measured on the same units have a joint distribution: how common each combination is. The scatter plot shows it. Each variable on its own has a marginal distribution, the histogram along its edge. And for each value of one variable, the other has a conditional distribution: the distribution in a thin slice.
Code
income, food = engel.income.to_numpy(), engel.foodexp.to_numpy()SLICES = [(300, 600), (600, 900), (900, 1300), (1300, 2600)]def plot_scatter_with_margins(fig: plt.Figure, spec) ->None:"""A scatter plot with histograms of each variable along its edges.""" grid = spec.subgridspec(2, 2, width_ratios=[4, 1], height_ratios=[1, 4], hspace=0.05, wspace=0.05 ) main = fig.add_subplot(grid[1, 0]) top = fig.add_subplot(grid[0, 0], sharex=main) side = fig.add_subplot(grid[1, 1], sharey=main) main.scatter(income, food, s=8, color=style.BLUE, alpha=0.5)for low, high in SLICES: main.axvspan( low, high, color=style.GREY, alpha=0.06if SLICES.index((low, high)) %2else0.14, lw=0, ) top.hist(income, bins=40, color=style.BLUE, alpha=0.55) side.hist(food, bins=40, color=style.BLUE, alpha=0.55, orientation="horizontal")for ax in (top, side): ax.set_axis_off() main.set( xlabel="household income", ylabel="food spending", xlim=(250, 2700), ylim=(150, 1700) ) top.set_title("Joint and marginal", loc="left")def plot_slices(ax: Axes) ->None:"""Food spending within each income slice, as boxplots.""" groups = [food[(income >= low) & (income < high)] for low, high in SLICES] ax.boxplot( groups, widths=0.5, patch_artist=True, boxprops=dict(facecolor=style.BLUE, alpha=0.3, color=style.INK), medianprops=dict(color=style.CORAL, lw=2), whiskerprops=dict(color=style.INK), capprops=dict(color=style.INK), flierprops=dict(markeredgecolor=style.INK), ) ax.set_xticks(range(1, 5), [f"{low} to {high}"for low, high in SLICES], fontsize=8.5) ax.set(title="Conditional on income", xlabel="income slice", ylabel="food spending")fig = plt.figure(figsize=(10, 3.9))outer = fig.add_gridspec(1, 2, width_ratios=[1, 1.1], wspace=0.3)plot_scatter_with_margins(fig, outer[0])plot_slices(fig.add_subplot(outer[1]))plt.show()
Figure 2.4.1: Left: income and food spending of the 235 households (the joint distribution), with each variable’s own histogram along the edges (the marginal distributions). Right: food spending within four income slices (conditional distributions). The slices move up with income, and they widen.
The conditional mean, the average food spending at each income, is exactly what the regression function of Chapter 1.3 describes: , read “the expected value of Y given that X equals x”. The vertical bar means “given”. Regression is the study of conditional distributions.
2.4.2 Independence
Two variables are independent when knowing one tells you nothing about the other: every conditional distribution is the same as the marginal. Then the joint probability is just the product of the two:
Correlation measures only straight-line dependence, so independence is stronger than zero correlation:
Figure 2.4.2: Left: the incomes paired with the food spending of a randomly chosen other household: independent by construction, a shapeless cloud. Middle: the real pairs: dependent. Right: dependent but uncorrelated: y is fully determined by x (a parabola plus a little noise), yet the correlation is about 0.
Independent implies uncorrelated, but not the other way round. The shuffled panel is also the picture behind the permutation tests of Chapter 3.1: shuffling one column makes the two independent, and shows what “no relation” looks like.
2.4.3 Conditional probability and Bayes’ rule
The probability of A given B is the share of B cases in which A also happens:
The order matters, and mixing it up is one of the most common errors in reasoning about chance. A screening test catches 90% of people with a disease and wrongly alarms 5% of healthy people. One person in a hundred has the disease. Someone tests positive. How likely is it that they have it?
Figure 2.4.3: 1,000 people, 10 of whom have the disease (red). The test flags 9 of the 10 sick people and 50 of the 990 healthy ones (circled). Of the 59 flagged people, only 9 are sick: a positive test means a 15% chance of disease, not 90%.
Counting is the safest way to get it right. Written as a formula, it is Bayes’ rule, which turns around into :
The base rate, how common the disease is to begin with, dominates. The same logic explains why a p-value is not the probability that the null hypothesis is true (Chapter 3.1), and why precision can be low for a rare class even with a good classifier (Chapter 6.1).
2.4.4 The multivariate normal
The normal distribution has a version for several variables at once. For two, its density looks like a hill, and its contours are ellipses. The means set the centre; the covariance matrix sets the shape:
The variances on the diagonal stretch the hill along each axis; the covariance tilts it.
Figure 2.4.4: Samples from two-variable normal distributions with standard deviations 1 and 1, and correlations 0, 0.6 and −0.9, with contours of the density. The arrows are the ellipse’s axes: the directions of largest and smallest spread, which principal component analysis finds (Chapter 7.1).
In a multivariate normal, every slice is normal, and the conditional mean of y given x is a straight line: linear regression is exactly right. With more variables the ellipse becomes an ellipsoid in more dimensions, still described by a mean vector and a covariance matrix.
2.4.5 Latent variables
Sometimes the most important variable is one we never measured. A latent variable is hidden, but leaves its mark on the variables we see. The two-humped petal lengths of Chapter 1.1 come from a hidden variable, the species. Suppose we did not know it. A mixture model assumes the data come from a few hidden groups, each with its own normal distribution, and finds the groups and each flower’s chance of belonging to them:
Code
petals = iris[["petal length (cm)", "petal width (cm)"]].to_numpy()mixture = GaussianMixture(3, n_init=5, random_state=0).fit(petals)found = mixture.predict(petals)order = np.argsort(mixture.means_[:, 0]) # name the groups from short to long petalsrelabel = np.empty(3, int)relabel[order] = np.arange(3)found = relabel[found]def draw_ellipses(ax: Axes) ->None:"""Two-standard-deviation ellipses of each fitted group.""" angles = np.linspace(0, 2* np.pi, 100)for g inrange(3): values, vectors = np.linalg.eigh(mixture.covariances_[g]) circle = np.c_[np.cos(angles), np.sin(angles)] *2* np.sqrt(values) ellipse = circle @ vectors.T + mixture.means_[g] ax.plot(ellipse[:, 0], ellipse[:, 1], color=SPECIES_COLORS[relabel[g]], lw=1.5)fig, axes = style.subplots(3, height=3.2, width=10.5, sharey=True)axes[0].scatter(petals[:, 0], petals[:, 1], s=10, color=style.GREY, alpha=0.7)axes[0].set_title("What we see")axes[1].scatter(petals[:, 0], petals[:, 1], s=10, c=SPECIES_COLORS[found], alpha=0.7)draw_ellipses(axes[1])axes[1].set_title("Groups found")axes[2].scatter(petals[:, 0], petals[:, 1], s=10, c=SPECIES_COLORS[iris.target], alpha=0.7)axes[2].set_title("True species")for ax in axes: ax.set_xlabel("petal length (cm)")axes[0].set_ylabel("petal width (cm)")fig.tight_layout()plt.show()
Figure 2.4.5: Petal length and width of the 150 flowers. Left: what we see, with no labels. Middle: a mixture of three two-variable normals fitted without labels; the ellipses are the groups it found, and each flower is coloured by its most likely group. Right: the true species. The hidden groups match the species closely.
A latent variable can also be a hidden amount rather than a group. Several test scores are correlated because they all depend on one underlying ability; several measurements of a flower are correlated because they all depend on its overall size. Factor analysis looks for a few such hidden amounts that explain the correlations among many measured ones:
Figure 2.4.6: Simulated: one hidden quantity (a flower’s overall size) drives three measurements, each with its own noise. Left: the structure, with the hidden variable in the dashed circle. Middle: the three measurements are correlated with each other. Right: once the hidden size is known, the correlations vanish: the measurements only ever shared the size.
That is the signature of a latent variable: correlations that disappear once it is held fixed. Principal components (Chapter 7.1) are a close relative, and many methods in machine learning, from mixture models to the hidden layers of a neural network (Chapter 8.1), are ways of finding useful hidden variables.
2.4.6 In one picture
The joint distribution shows combinations; marginals show each variable alone; conditional distributions show one variable within a slice of another.
Regression is the conditional mean: .
Independent means every slice looks the same; independence implies zero correlation, not the reverse.
Bayes’ rule turns into ; the base rate matters.
A multivariate normal is set by a mean and a covariance matrix; its contours are ellipses.
Latent variables are hidden causes; mixture models and factor analysis look for them.
Next: extreme values, the laws of the largest observations.