Skip to content
The Lumenary
Grades 6–8Ages 11–14 60 min

Statistics: centre, spread and distributions

Data vary. How to summarise a whole distribution with a few numbers and a picture — and why the mean and the median answer different questions.

Formally verified · 18 LeanComputationally tested · 2 PythonAI-drafted · awaiting human reviewMathematically reviewed: Awaiting human review

Your guides: Chloe, Margaret, Gideon

MotivationH

Questions that expect different answers

Compare two questions:

  1. How many minutes did it take Thandiwe to get to school this morning?
  2. How many minutes does it take students in our class to get to school?

The first has one answer: Thandiwe can look at a clock. The second has many answers, because students live in different places and travel in different ways. It is a statistical question: it expects variability in the data, and the answer is not one number but a description of how the data are spread out.

Statistical questions are everywhere. How much rain falls in Nairobi in April? How tall are ten-year-olds in Peru? Which of two maize varieties gives a bigger harvest? To answer them we collect data, picture its distribution, and summarise it with a few well-chosen numbers. Choosing them well — and knowing what each one hides — is what this lesson is about.

IntuitionH

Seeing a distribution

Here are the travel times, in minutes, of the 15 students in one class (class A), sorted:

5, 8, 10, 10, 12, 12, 12, 15, 15, 18, 20, 20, 25, 30, 45.5,\ 8,\ 10,\ 10,\ 12,\ 12,\ 12,\ 15,\ 15,\ 18,\ 20,\ 20,\ 25,\ 30,\ 45.

A dot plot puts one dot per student above a number line.

01020304050 minmedianmean
Class A's travel times as a dot plot. Most students take 10–20 minutes; one student, who lives far away, takes 45. The median (15) is marked below the line in blue, the mean (about 17.1) in gold.

Three things to notice about any distribution:

  • Centre: where is a typical value? Here, around 15 minutes.
  • Spread: how much do the values vary? Here, most are between 10 and 20 minutes, but the whole range is 40 minutes.
  • Shape: is it symmetric, or does it trail off to one side? Are there outliers, values far from the rest? Here the data trail off to the right, and 45 stands apart.
DefinitionsH

Definitions

Definition— statistical question

A statistical question is one that anticipates variability in the data used to answer it. “How tall am I?” is not statistical; “How tall are the students in my school?” is.

Let x1,x2,…,xnx_1, x_2, \ldots, x_n be nn numerical data values.

Definition— mean, median, mode

  • The mean is xˉ=x1+x2+⋯+xnn\bar x = \dfrac{x_1 + x_2 + \cdots + x_n}{n}: the fair share if the total were shared out equally, as in data displays and the mean as a fair share. The mean of whole numbers is often not a whole number but a rational number, such as 25715\tfrac{257}{15}.
  • The median is the middle value when the data are sorted; if nn is even, it is the mean of the two middle values.
  • A mode is a value that occurs most often. (There may be several modes, or — if every value occurs once — none worth reporting.)

Definition— range, quartiles, interquartile range

  • The range is the largest value minus the smallest.
  • The lower quartile Q1Q_1 is the median of the lower half of the sorted data, and the upper quartile Q3Q_3 the median of the upper half. In this lesson, when nn is odd the median itself is left out of both halves.
  • The interquartile range is IQR=Q3−Q1\mathrm{IQR} = Q_3 - Q_1: the spread of the middle half of the data.

Note

Statisticians and software use several slightly different rules for quartiles. They agree on large data sets but can differ a little on small ones. Always say which rule you use; the computation section shows three rules disagreeing.

Definition— five-number summary, box plot, outlier

The five-number summary is: minimum, Q1Q_1, median, Q3Q_3, maximum. A box plot draws it: a box from Q1Q_1 to Q3Q_3 with a line at the median, and whiskers out to the smallest and largest values. An outlier is a value far from the rest. A common rule of thumb (due to John Tukey) calls a value an outlier if it is more than 1.5×IQR1.5 \times \mathrm{IQR} below Q1Q_1 or above Q3Q_3; outliers are then drawn as separate dots and the whiskers stop at the most extreme values that are not outliers.

A histogram groups numerical data into intervals of equal width (bins) and draws a bar for each bin whose height is the number of values in it. In this lesson a bin includes its left end but not its right end.

Worked examplesH

Worked examples

Example— Class A, summarised

For the 15 travel times above:

  • Mean: the total is 257257, so xˉ=25715≈17.1\bar x = \tfrac{257}{15} \approx 17.1 minutes.
  • Median: the 8th of 15 sorted values, 1515 minutes.
  • Mode: 1212 minutes (three students).
  • Range: 45−5=4045 - 5 = 40 minutes.
  • Quartiles: the lower half is 5,8,10,10,12,12,125, 8, 10, 10, 12, 12, 12, so Q1=10Q_1 = 10; the upper half is 15,18,20,20,25,30,4515, 18, 20, 20, 25, 30, 45, so Q3=20Q_3 = 20. The IQR is 1010 minutes.
  • Outliers: Q3+1.5×IQR=20+15=35Q_3 + 1.5 \times \mathrm{IQR} = 20 + 15 = 35, and 45>3545 > 35, so 4545 is an outlier. Nothing is below Q1−15=−5Q_1 - 15 = -5.

Example— Comparing two classes with box plots

Class B, at a school where most students walk, recorded these times: 10,15,15,18,20,20,22,25,25,25,28,30,30,35,4010, 15, 15, 18, 20, 20, 22, 25, 25, 25, 28, 30, 30, 35, 40. Its five-number summary is 10,18,25,30,4010, 18, 25, 30, 40, its IQR is 1212, its mean about 23.923.9, and it has no outliers.

AB01020304050 min
Box plots of the two classes on the same scale. Class B's median (25 minutes) is higher than class A's (15), and its middle half is a little more spread out (IQR 12 against 10). Class A has an outlier at 45.

A fair comparison mentions both centre and spread: “Class B's students typically take about 10 minutes longer (median 25 against 15), with a similar spread in the middle half (IQR 12 against 10); class A has one student with an unusually long journey.”

Example— Same mean, different spread

Two bus routes are timed over 10 days. Waiting times in minutes:

  • Route 1: 9,10,10,11,9,10,11,10,9,119, 10, 10, 11, 9, 10, 11, 10, 9, 11 — mean 1010, range 22.
  • Route 2: 2,18,5,15,10,1,19,8,12,102, 18, 5, 15, 10, 1, 19, 8, 12, 10 — mean 1010, range 1818.

The means (and even the medians) are equal, but route 1 is reliable and route 2 is not. Reporting only the mean would hide the most important difference.

Example— Choosing a summary

  • Household incomes, house prices and journey times are usually skewed: a few very large values. The median describes a typical household better than the mean, which the largest values pull upwards.
  • When the total matters — the fuel a fleet of buses uses, the rice a family eats in a month — the mean is the right tool, because mean × n=\times\, n = total.
  • For categories, such as favourite sports, only the mode makes sense: there is no “mean sport”.
ProofH

Why the summaries behave as they do

Theorem— Theorem 1 (the mean is the balance point)

The deviations from the mean add up to zero: ∑i=1n(xi−xˉ)=0\displaystyle\sum_{i=1}^n (x_i - \bar x) = 0.

Proof

∑i=1n(xi−xˉ)=∑i=1nxi−nxˉ=∑i=1nxi−∑i=1nxi=0\displaystyle\sum_{i=1}^n (x_i - \bar x) = \sum_{i=1}^n x_i - n\bar x = \sum_{i=1}^n x_i - \sum_{i=1}^n x_i = 0, because nxˉn \bar x is the total.

∎

So the values above the mean exactly balance the values below it. If the dot plot were a see-saw with equal weights at the dots, it would balance at the mean.

Theorem— Theorem 2 (shifting and scaling)

If a number cc is added to every value, the mean, the median and the mode increase by cc, while the range and the IQR do not change. If every value is multiplied by a positive number aa, all five of these summaries are multiplied by aa.

Proof

The new mean is 1n∑(xi+c)=1n∑xi+1n⋅nc=xˉ+c\tfrac{1}{n}\sum (x_i + c) = \tfrac1n \sum x_i + \tfrac1n \cdot nc = \bar x + c, and similarly 1n∑axi=axˉ\tfrac1n \sum a x_i = a \bar x. Adding cc (or multiplying by a>0a > 0) does not change the order of the values, or which values are equal, so the median, the quartiles and the mode are the old ones plus cc (or times aa). A difference such as Q3−Q1Q_3 - Q_1 is unchanged by adding cc to both, and multiplied by aa when both are.

∎

Converting travel times from minutes to hours (a=160a = \tfrac{1}{60}) therefore needs no recomputation.

Theorem— Theorem 3 (the mean feels every value)

If one value is increased by dd, the mean increases by dn\dfrac dn.

Proof

The total increases by dd, and the mean is the total divided by nn.

∎

Theorem— Theorem 4 (the median resists outliers)

Suppose there are n≥3n \ge 3 values. Replacing the largest value by any larger value does not change the median.

Proof

The sorted order is unchanged: the largest value stays in the last position, number nn. The median uses only the middle position — position n+12\tfrac{n+1}{2} if nn is odd, positions n2\tfrac n2 and n2+1\tfrac n2 + 1 if nn is even — and for n≥3n \ge 3 these all come before position nn. So the median is computed from the same values as before.

∎

In class A, changing 4545 to 900900 moves the mean from about 17.117.1 to about 74.174.1 minutes (Theorem 3: up by 85515=57\tfrac{855}{15} = 57) but leaves the median at 1515. A summary that hardly reacts to a few extreme values is called resistant: the median is resistant and the mean is not.

Theorem— Theorem 5 (same centre, different spread)

Two data sets can have the same mean and the same median but very different ranges and IQRs.

Proof

The bus routes are an example: both have mean 1010 and median 1010, but ranges 22 and 1818 and IQRs 22 and 1010.

∎

Going further: two best centres

Suppose we must choose one number cc to stand for all the data. How should we measure the error? Two natural choices give the two centres.

Theorem— Theorem 6 (the mean minimises squared distance)

For every number cc,

∑i=1n(xi−c)2=∑i=1n(xi−xˉ)2+n(xˉ−c)2.\sum_{i=1}^n (x_i - c)^2 = \sum_{i=1}^n (x_i - \bar x)^2 + n(\bar x - c)^2.

Hence ∑(xi−c)2\sum (x_i - c)^2 is smallest exactly when c=xˉc = \bar x.

Proof

Write xi−c=(xi−xˉ)+(xˉ−c)x_i - c = (x_i - \bar x) + (\bar x - c) and square:

(xi−c)2=(xi−xˉ)2+2(xi−xˉ)(xˉ−c)+(xˉ−c)2.(x_i - c)^2 = (x_i - \bar x)^2 + 2(x_i - \bar x)(\bar x - c) + (\bar x - c)^2.

Add these up for i=1,…,ni = 1, \ldots, n. The middle terms give 2(xˉ−c)∑(xi−xˉ)2(\bar x - c)\sum (x_i - \bar x), which is 00 by Theorem 1. The last terms give n(xˉ−c)2n(\bar x - c)^2. This proves the identity. The extra term n(xˉ−c)2n(\bar x - c)^2 is never negative, and it is zero only when c=xˉc = \bar x.

∎

Theorem— Theorem 7 (the median minimises total distance)

The total distance ∑i=1n∣xi−c∣\sum_{i=1}^n |x_i - c| is smallest when cc is the median.

Proof

Sort the values, x1≤x2≤⋯≤xnx_1 \le x_2 \le \cdots \le x_n, and pair the smallest with the largest, the second smallest with the second largest, and so on. For a pair a≤ba \le b, the distance from cc to aa plus the distance from cc to bb is at least b−ab - a, with equality exactly when cc lies between aa and bb. The intervals [x1,xn]⊇[x2,xn−1]⊇⋯[x_1, x_n] \supseteq [x_2, x_{n-1}] \supseteq \cdots are nested, and the median lies in all of them. (If nn is odd, the middle value is left unpaired; its distance ∣xmid−c∣|x_{\text{mid}} - c| is zero exactly when cc is the median.) So at the median every pair achieves its smallest possible total, and no other choice of cc can do better.

∎

When nn is even, every cc between the two middle values gives the same smallest total — another reason the median of an even number of values is a convention.

Formal mathematicsF

Statistics in Lean

A data set is a list of rational numbers, so all the arithmetic is exact. The mean is the sum divided by the length.

meanLean 4Formally verified
/-- The **mean** of a list of numbers: their sum divided by how many there are.
(For the empty list Lean's convention `0 / 0 = 0` gives `0`.) -/
def mean (xs : List Rat) : Rat := xs.sum / xs.length
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
mean_mul_lengthLean 4Proved here from basic factsFormally verified
/-- Mean times number of values equals the total: share the total out fairly, and each of the
`n` values gets the mean. -/
theorem mean_mul_length (xs : List Rat) (h : xs ≠ []) : mean xs * xs.length = xs.sum := by
  have hn : (xs.length : Rat) ≠ 0 := by
    have : xs.length ≠ 0 := fun h0 => h (List.eq_nil_of_length_eq_zero h0)
    simpa using this
  unfold mean
  rw [Rat.div_mul_cancel hn]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor

Theorem 1, via a lemma proved by induction on the list:

sum_map_subLean 4Proved here from basic factsFormally verified
/-- Subtracting `c` from every value lowers the total by `n · c`:
`Σ (xᵢ - c) = Σ xᵢ - n · c`. Proof by induction on the list. -/
theorem sum_map_sub (xs : List Rat) (c : Rat) :
    (xs.map (fun x => x - c)).sum = xs.sum - xs.length * c := by
  induction xs with
  | nil => simp [Rat.sub_self]
  | cons x xs ih =>
    simp only [List.map_cons, List.sum_cons, List.length_cons, ih]
    push_cast
    grind
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
sum_deviations_zeroLean 4Proved here from basic factsFormally verified
/-- **The mean is the balance point.** The deviations from the mean add up to zero:
`Σ (xᵢ - x̄) = Σ xᵢ - n · x̄ = 0`. Values above the mean exactly balance values below it. -/
theorem sum_deviations_zero (xs : List Rat) : (xs.map (fun x => x - mean xs)).sum = 0 := by
  rw [sum_map_sub]
  cases xs with
  | nil => simp [Rat.sub_self]
  | cons y ys =>
    rw [Rat.mul_comm, mean_mul_length (y :: ys) (List.cons_ne_nil y ys), Rat.sub_self]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor

Theorem 2 for the mean, and Theorem 3:

mean_add_constLean 4Proved here from basic factsFormally verified
/-- **Shifting the data shifts the mean.** Adding `c` to every value adds `c` to the mean. -/
theorem mean_add_const (xs : List Rat) (h : xs ≠ []) (c : Rat) :
    mean (xs.map (fun x => x + c)) = mean xs + c := by
  have hn : (xs.length : Rat) ≠ 0 := by
    have : xs.length ≠ 0 := fun h0 => h (List.eq_nil_of_length_eq_zero h0)
    simpa using this
  have hsum : (xs.map (fun x => x + c)).sum = xs.sum + xs.length * c := by
    have := sum_map_sub xs (-c)
    simp only [Rat.sub_eq_add_neg, Rat.neg_neg] at this
    grind
  unfold mean
  rw [hsum, List.length_map]
  grind
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
mean_scaleLean 4Proved here from basic factsFormally verified
/-- **Scaling the data scales the mean.** Multiplying every value by `a` (for instance,
converting hours to minutes with `a = 60`) multiplies the mean by `a`. -/
theorem mean_scale (xs : List Rat) (a : Rat) : mean (xs.map (fun x => a * x)) = a * mean xs := by
  have hsum : (xs.map (fun x => a * x)).sum = a * xs.sum := by
    induction xs with
    | nil => simp
    | cons x xs ih =>
      simp only [List.map_cons, List.sum_cons, ih]
      rw [Rat.mul_add]
  unfold mean
  rw [hsum, List.length_map, Rat.div_def, Rat.div_def, Rat.mul_assoc]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
mean_change_lastLean 4Proved here from basic factsFormally verified
/-- **The mean is sensitive to every value.** Increasing one value by `d` (here, the last one)
increases the mean by `d / n`, where `n` is the number of values. An outlier `d` units away
drags the mean `d / n` units with it. -/
theorem mean_change_last (xs : List Rat) (y d : Rat) :
    mean (xs ++ [y + d]) = mean (xs ++ [y]) + d / (xs.length + 1) := by
  unfold mean
  simp only [List.sum_append, List.sum_cons, List.sum_nil, List.length_append,
    List.length_cons, List.length_nil]
  push_cast
  have hpos : (0 : Rat) < (xs.length : Rat) + 1 := by
    have := (Rat.natCast_nonneg (a := xs.length))
    grind
  have hne : (xs.length : Rat) + 1 ≠ 0 := Rat.ne_of_gt hpos
  grind
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor

The median is defined for a list that is already sorted, by position. Theorem 4 then becomes a statement about positions: changing the last entry does not touch the middle ones.

medianSortedLean 4Formally verified
/-- The **median** of a list that is already sorted from smallest to largest: the middle value
if there is an odd number of values, otherwise the mean of the two middle values.
(`l.getD i 0` is the value in position `i`, counting from `0`.) -/
def medianSorted (l : List Rat) : Rat :=
  if l.length % 2 = 1 then l.getD (l.length / 2) 0
  else (l.getD (l.length / 2 - 1) 0 + l.getD (l.length / 2) 0) / 2
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
median_resistantLean 4Proved here from basic factsFormally verified
/-- **The median resists outliers.** In a sorted list of at least three values, replacing the
largest value (position `n - 1`) by any value `v` does not change the median. The median
only looks at the middle position(s), `n/2` and possibly `n/2 - 1`, and both are before
`n - 1` once `n ≥ 3`. If `v` is at least the old largest value, the list stays sorted, so this
says: making the largest value as extreme as you like leaves the median alone. -/
theorem median_resistant (l : List Rat) (hl : 3 ≤ l.length) (v : Rat) :
    medianSorted (l.set (l.length - 1) v) = medianSorted l := by
  have h1 : l.length / 2 ≠ l.length - 1 := by omega
  have h2 : l.length / 2 - 1 ≠ l.length - 1 := by omega
  unfold medianSorted
  simp only [List.length_set, List.getD_eq_getElem?_getD,
    List.getElem?_set_ne (Ne.symm h1), List.getElem?_set_ne (Ne.symm h2)]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
outlier_exampleLean 4Proved here from basic factsFormally verified
/-- A worked example, computed by Lean. Minutes to walk to school for seven students: the
median is `5` and the mean is `38/7 ≈ 5.4`. Replace the largest value `8` by an outlier `80`:
the median is still `5`, but the mean jumps to `110/7 ≈ 15.7`. -/
theorem outlier_example :
    medianSorted [3, 4, 5, 5, 6, 7, 8] = 5 ∧ mean [3, 4, 5, 5, 6, 7, 8] = 38 / 7 ∧
    medianSorted [3, 4, 5, 5, 6, 7, 80] = 5 ∧ mean [3, 4, 5, 5, 6, 7, 80] = 110 / 7 := by
  decide +kernel
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor

Quartiles with this lesson's convention, and Lean's computation of class A's summary:

lowerHalfLean 4Formally verified
/-- The lower half of a sorted list: the values before the median position. For an odd number
of values the median itself is left out (the convention used in this lesson). -/
def lowerHalf (l : List Rat) : List Rat := l.take (l.length / 2)
Axioms used: No axiomsformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
upperHalfLean 4Formally verified
/-- The upper half of a sorted list: the values after the median position. -/
def upperHalf (l : List Rat) : List Rat := l.drop ((l.length + 1) / 2)
Axioms used: No axiomsformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
iqrLean 4Formally verified
/-- The **interquartile range** `Q₃ - Q₁` of a sorted list, where the quartiles `Q₁` and `Q₃` are
the medians of the lower and upper halves. -/
def iqr (l : List Rat) : Rat := medianSorted (upperHalf l) - medianSorted (lowerHalf l)
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
classALean 4Formally verified
/-- The lesson's data: minutes taken to get to school by 15 students, sorted. -/
def classA : List Rat := [5, 8, 10, 10, 12, 12, 12, 15, 15, 18, 20, 20, 25, 30, 45]
Axioms used: propext, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
class_a_summaryLean 4Proved here from basic factsFormally verified
/-- The summary of `classA`, computed by Lean: mean `257/15 ≈ 17.1`, median `15`, quartiles
`Q₁ = 10` and `Q₃ = 20`, so the interquartile range is `10`. -/
theorem class_a_summary :
    mean classA = 257 / 15 ∧ medianSorted classA = 15 ∧
    medianSorted (lowerHalf classA) = 10 ∧ medianSorted (upperHalf classA) = 20 ∧
    iqr classA = 10 := by
  decide +kernel
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor

Theorem 6, the mean as the best centre for squared distances:

sum_sq_expandLean 4Proved here from basic factsFormally verified
/-- Expanding squares: `Σ (xᵢ - c)² = Σ xᵢ² - 2c · Σ xᵢ + n · c²`. Proof by induction, expanding
`(x - c)² = x² - 2cx + c²` for one value at a time. -/
theorem sum_sq_expand (xs : List Rat) (c : Rat) :
    (xs.map (fun x => (x - c) * (x - c))).sum
      = (xs.map (fun x => x * x)).sum - 2 * c * xs.sum + xs.length * (c * c) := by
  induction xs with
  | nil => simp [Rat.sub_self, Rat.add_zero]
  | cons x xs ih =>
    simp only [List.map_cons, List.sum_cons, List.length_cons, ih]
    push_cast
    grind
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
sum_sq_devLean 4Proved here from basic factsFormally verified
/-- The total squared distance from any number `c` is the total squared distance from the mean
`m`, plus `n · (m - c)²`:
`Σ (xᵢ - c)² = Σ (xᵢ - m)² + n · (m - c)²`. Expand both sides with `sum_sq_expand` and use
`Σ xᵢ = n · m`. -/
theorem sum_sq_dev (xs : List Rat) (h : xs ≠ []) (c : Rat) :
    (xs.map (fun x => (x - c) * (x - c))).sum
      = (xs.map (fun x => (x - mean xs) * (x - mean xs))).sum
        + xs.length * ((mean xs - c) * (mean xs - c)) := by
  have hm : mean xs * xs.length = xs.sum := mean_mul_length xs h
  rw [sum_sq_expand xs c, sum_sq_expand xs (mean xs)]
  grind
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor
mean_minimises_sqLean 4Proved here from basic factsFormally verified
/-- **The mean is the best centre for squared distances.** For every number `c`, the total
squared distance of the data from `c` is at least the total squared distance from the mean.
This follows from `sum_sq_dev`, because `n · (m - c)² ≥ 0`. -/
theorem mean_minimises_sq (xs : List Rat) (h : xs ≠ []) (c : Rat) :
    (xs.map (fun x => (x - mean xs) * (x - mean xs))).sum
      ≤ (xs.map (fun x => (x - c) * (x - c))).sum := by
  rw [sum_sq_dev xs h c]
  have hnn : 0 ≤ (xs.length : Rat) * ((mean xs - c) * (mean xs - c)) :=
    Rat.mul_nonneg Rat.natCast_nonneg (mul_self_nonneg _)
  grind
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/StatisticsIntro.leanTry it in the Lean web editor

Not formalised: Theorem 2 for the median and quartiles, and Theorem 7 (the median minimises total distance), both of which need sorting.

ComputationC

Watching the summaries move

Mean, median and spread: what an outlier changesPythonExact computationComputationally tested
"""Centre and spread, and what one extreme value does to them.

For the lesson's data (minutes taken to get to school by 15 students) this
experiment computes the mean, median, mode, range, quartiles and
interquartile range exactly, using the lesson's convention for quartiles
(medians of the lower and upper halves, leaving out the median when the
number of values is odd). It then makes the largest value more and more
extreme and records which summaries move. Two side notes: other quartile
conventions (Python's own) give slightly different answers on some data,
and two bus routes with the same mean can have very different spreads.
"""

import statistics
from fractions import Fraction

CLASS_A = [5, 8, 10, 10, 12, 12, 12, 15, 15, 18, 20, 20, 25, 30, 45]
ROUTE_1 = [9, 10, 10, 11, 9, 10, 11, 10, 9, 11]
ROUTE_2 = [2, 18, 5, 15, 10, 1, 19, 8, 12, 10]

def mean(values):
    """Exact mean as a Fraction."""
    if not values:
        raise ValueError("no data")
    return Fraction(sum(values), len(values))

def median(values):
    """Middle value of the sorted data, or the mean of the two middle ones."""
    s = sorted(values)
    n = len(s)
    if n == 0:
        raise ValueError("no data")
    if n % 2 == 1:
        return Fraction(s[n // 2])
    return Fraction(s[n // 2 - 1] + s[n // 2], 2)

def quartiles(values):
    """(Q1, Q3): medians of the lower and upper halves (median excluded)."""
    s = sorted(values)
    n = len(s)
    if n < 2:
        raise ValueError("need at least two values")
    return median(s[: n // 2]), median(s[(n + 1) // 2:])

def summary(values):
    """Dictionary of the lesson's summaries of centre and spread."""
    q1, q3 = quartiles(values)
    return {
        "mean": mean(values),
        "median": median(values),
        "modes": statistics.multimode(values),
        "range": max(values) - min(values),
        "Q1": q1,
        "Q3": q3,
        "IQR": q3 - q1,
    }

def outliers(values):
    """Values more than 1.5 IQR below Q1 or above Q3 (Tukey's rule)."""
    q1, q3 = quartiles(values)
    spread = q3 - q1
    low, high = q1 - Fraction(3, 2) * spread, q3 + Fraction(3, 2) * spread
    return [v for v in values if v < low or v > high]

def show(x):
    """A Fraction as an integer, or a decimal to 2 places."""
    x = Fraction(x)
    return str(x.numerator) if x.denominator == 1 else f"{float(x):.2f}"

def main():
    s = summary(CLASS_A)
    print("Minutes to school, 15 students:", " ".join(map(str, CLASS_A)))
    print(f"  mean {show(s['mean'])}  median {show(s['median'])}  mode "
          f"{s['modes']}  range {s['range']}")
    print(f"  Q1 {show(s['Q1'])}  Q3 {show(s['Q3'])}  IQR {show(s['IQR'])}"
          f"  outliers (1.5 IQR rule): {outliers(CLASS_A)}")
    print()
    print("Make the largest value more extreme:")
    print("  largest   mean    median   range   IQR")
    for big in (45, 90, 180, 900):
        data = CLASS_A[:-1] + [big]
        t = summary(data)
        print(f"  {big:7d}  {show(t['mean']):>6}   {show(t['median']):>5}"
              f"   {t['range']:5d}   {show(t['IQR']):>3}")
    print()
    r2 = sorted(ROUTE_2)
    py_ex = statistics.quantiles(r2, n=4, method="exclusive")
    py_in = statistics.quantiles(r2, n=4, method="inclusive")
    q1, q3 = quartiles(r2)
    print("Quartile conventions differ. Route 2, sorted:")
    print("  " + " ".join(map(str, r2)))
    print(f"  halves (this lesson): Q1 = {show(q1)}, Q3 = {show(q3)}")
    print(f"  Python 'exclusive':   Q1 = {py_ex[0]:g}, Q3 = {py_ex[2]:g}")
    print(f"  Python 'inclusive':   Q1 = {py_in[0]:g}, Q3 = {py_in[2]:g}")
    print()
    print("Two bus routes, waiting times over 10 days (minutes):")
    for name, data in (("route 1", ROUTE_1), ("route 2", ROUTE_2)):
        t = summary(data)
        print(f"  {name}: mean {show(t['mean'])}, median {show(t['median'])},"
              f" range {t['range']}, IQR {show(t['IQR'])}")
    print("  Same centre, very different reliability.")
    print()
    print("Epistemic status: exact arithmetic on small illustrative data sets.")
    print("That the median ignores the largest value is proved in the lesson")
    print("(for three or more values); this run only illustrates it, and the")
    print("IQR's behaviour here depends on the data. Quartiles also depend on")
    print("a chosen convention.")

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

The mean chases the outlier; the median and IQR stay put. And three respectable quartile rules give three different answers for the same ten numbers — a reminder that some “facts” in statistics are conventions.

The mean and the median as best centresPythonExact computationExhaustive finite checkComputationally tested
"""Two "best centres": the mean and the median as answers to different questions.

Pick a number c to stand for the whole data set. If we measure the error
by the total squared distance  sum (x - c)^2,  the best c is the mean. If we
measure it by the total distance  sum |x - c|,  the best c is the median.
This experiment scans c over a fine grid (exact fractions) for the lesson's
data, finds where each total is smallest, and checks the identity
sum (x - c)^2 = sum (x - mean)^2 + n (mean - c)^2 at every grid point.
"""

from fractions import Fraction

DATA = [5, 8, 10, 10, 12, 12, 12, 15, 15, 18, 20, 20, 25, 30, 45]

def total_distance(values, c):
    """Sum of |x - c|."""
    return sum(abs(Fraction(x) - c) for x in values)

def total_squared_distance(values, c):
    """Sum of (x - c)^2."""
    return sum((Fraction(x) - c) ** 2 for x in values)

def grid(low, high, step):
    """Exact grid low, low + step, ..., high."""
    count = int((Fraction(high) - Fraction(low)) / Fraction(step))
    return [Fraction(low) + k * Fraction(step) for k in range(count + 1)]

def minimisers(values, cost, candidates):
    """The candidates at which cost(values, c) is smallest."""
    costs = [(cost(values, c), c) for c in candidates]
    best = min(k for k, _ in costs)
    return [c for k, c in costs if k == best]

def identity_holds(values, candidates):
    """Check sum (x-c)^2 == sum (x-m)^2 + n (m-c)^2 at every candidate."""
    n = len(values)
    m = Fraction(sum(values), n)
    base = total_squared_distance(values, m)
    return all(total_squared_distance(values, c) == base + n * (m - c) ** 2
               for c in candidates)

def show(x):
    """A Fraction as an integer or a decimal to 2 places."""
    x = Fraction(x)
    return str(x.numerator) if x.denominator == 1 else f"{float(x):.2f}"

def main():
    m = Fraction(sum(DATA), len(DATA))
    print("Data (minutes to school):", " ".join(map(str, DATA)))
    print(f"mean = {m} = {show(m)}, median = 15")
    print()
    print("     c    total distance   total squared distance")
    for c in (10, 15, m, 20, 25):
        print(f"  {show(c):>5}   {show(total_distance(DATA, c)):>14}   "
              f"{show(total_squared_distance(DATA, c)):>22}")
    print()
    cands = grid(0, 50, Fraction(1, 4))
    print(f"Scanning c = 0, 1/4, 1/2, ..., 50 ({len(cands)} values):")
    best_abs = minimisers(DATA, total_distance, cands)
    best_sq = minimisers(DATA, total_squared_distance, cands)
    print(f"  smallest total distance at c = {[show(c) for c in best_abs]}")
    print(f"  smallest total squared distance at c = "
          f"{[show(c) for c in best_sq]}")
    print("    (the grid point nearest to the mean)")
    print(f"  identity checked at every grid point: "
          f"{identity_holds(DATA, cands)}")
    even = [1, 2, 6, 10]
    flat = minimisers(even, total_distance, grid(0, 12, Fraction(1, 2)))
    print(f"For {even} (even count) every c from {show(flat[0])} to "
          f"{show(flat[-1])} ties:")
    print("  the total distance is flat between the two middle values.")
    print()
    print("Epistemic status: exact arithmetic, exhaustive over a finite grid")
    print("of candidates. The lesson proves that the mean minimises the total")
    print("squared distance for every real c (the identity above), and why")
    print("the median minimises the total distance.")

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

The scan finds the median and (the grid point nearest to) the mean, as Theorems 6 and 7 predict. A scan over 201 points is finite evidence; the theorems cover every cc.

Explore

Explore: move the outlier

Class A: minutes to get to school

Show the same data as tallies, pictures, bars or dots, and find the mean, median and mode.

51015202530354045mean ≈17.13median 15

Mean: total ÷ number of values

257 ÷ 15 ≈ 17.13 min

Median: the middle value when the values are sorted = 15 min

5810101212121515182020253045

It is value number 8 of 15 in the sorted list.

Open “Change the data”, remove the 4545 and add a much larger number, such as 9090 or 200200: watch the mean marker slide while the median stays still. Then remove the outlier altogether. Switch to the histogram and change the bin width: the same data can look quite different, so always say what bin width you used.

Exercises

Exercises

1. Statistics: centre, spread and distributions

Warm-upHuman

Which of these are statistical questions? (Choose all that apply.)

Choose all that apply

2. Statistics: centre, spread and distributions

Warm-upHuman

Six friends picked these numbers of mangoes: 4,9,6,3,8,64, 9, 6, 3, 8, 6. What is the mean number of mangoes per friend?

3. Statistics: centre, spread and distributions

CoreHuman

Find the median of 12,7,3,20,9,1512, 7, 3, 20, 9, 15.

Choose one

4. Statistics: centre, spread and distributions

CoreHuman

Using this lesson's rule for quartiles, find the interquartile range of 3,5,7,8,10,12,13,15,193, 5, 7, 8, 10, 12, 13, 15, 19.

5. Statistics: centre, spread and distributions

CoreHuman

The data 3,4,5,5,6,7,83, 4, 5, 5, 6, 7, 8 are minutes to walk to school. The 88 is a typing error and should have been 8080. Which summaries change when it is corrected? (Choose all that apply.)

Choose all that apply

6. Statistics: centre, spread and distributions

CoreHuman

Two maize varieties are grown on 20 plots each. Variety P: median yield 3.13.1 tonnes per hectare, IQR 0.40.4. Variety Q: median 3.23.2, IQR 1.81.8. Which statement is the best comparison?

Choose one

7. Statistics: centre, spread and distributions

StretchHuman

In a test, a class of 1010 students has a mean score of 6060 and a class of 3030 students has a mean score of 8080. What is the mean score of all 4040 students?

8. Statistics: centre, spread and distributions

StretchHuman

In the proof of Theorem 6 we expand ∑(xi−c)2=∑(xi−xˉ)2+2(xˉ−c)∑(xi−xˉ)+n(xˉ−c)2\sum (x_i - c)^2 = \sum (x_i - \bar x)^2 + 2(\bar x - c)\sum (x_i - \bar x) + n(\bar x - c)^2. Why does the middle term vanish?

Choose one

9. Statistics: centre, spread and distributions

CoreFormal

In Lean, median_resistant says: 3 ≤ l.length → medianSorted (l.set (l.length - 1) v) = medianSorted l. Why is the hypothesis 3 ≤ l.length needed?

Choose one

10. Statistics: centre, spread and distributions

StretchComputational

Write five_number_summary(data) returning a tuple (minimum, Q1, median, Q3, maximum) using this lesson's rule: sort the data; the median is the middle value (or the mean of the two middle values); Q1Q_1 and Q3Q_3 are the medians of the lower and upper halves, leaving out the median when the number of values is odd. Use a helper median(values).

Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.
Common misconceptionsH

Common misconceptions

Misconception

“The mean is always the best summary of the centre.” For skewed data or data with outliers, the median is often more typical: a few very rich households raise the mean income far above what most households earn. Choose the summary that fits the question.

Misconception

Comparing groups by their centres alone. The two bus routes have the same mean and median, yet one is reliable and the other is not. A good comparison reports centre and spread, and looks at the whole distribution.

Misconception

Taking the middle of the unsorted list. The median of 7,2,97, 2, 9 is 77 (sort first: 2,7,92, 7, 9), not 22.

Misconception

“The mean of two group means is the overall mean.” If a class of 1010 has mean 6060 and a class of 3030 has mean 8080, the overall mean is 10×60+30×8040=75\tfrac{10 \times 60 + 30 \times 80}{40} = 75, not 7070. The larger group counts for more.

Misconception

“The mode is the typical value.” In 1,1,7,8,9,101, 1, 7, 8, 9, 10 the mode is 11, which is not typical at all. The mode is most useful for categories.

ViewpointsH

Other ways of seeing

Physics— The mean is a centre of mass

Put equal weights at the positions x1,…,xnx_1, \ldots, x_n on a light rod. The rod balances at the point cc where the turning effects on the two sides cancel: ∑(xi−c)=0\sum (x_i - c) = 0. By Theorem 1 that point is the mean. So the mean is the centre of mass of the data, and an outlier, like a weight far out on a see-saw, has a large effect. (The physics is an analogy that explains the formula; the proof is Theorem 1.)

Geometry— Two ways to measure distance

Theorem 6 is a form of Pythagoras' theorem: the total squared distance from cc splits into the total squared distance from xˉ\bar x plus a square, with no cross term, because the deviations xi−xˉx_i - \bar x add up to zero. In higher dimensions this becomes the method of least squares, used to fit lines to data. Theorem 7 answers a different geometric question: where on a straight road should a village build one well to make the total walking distance of its houses as small as possible? At the median house. Squared distances and plain distances lead to different “centres”.

Probability— Data as a sample

Usually our data are a sample from a larger population — 15 students out of a whole school. Had we chosen 15 other students, the mean and median would have come out a little different. Statistics asks how much such summaries vary from sample to sample, and so how far they can be trusted; see sampling and statistical reasoning and probability.

History & global context

History and global context

  • Counting people and harvests. States have collected data for thousands of years — censuses in ancient Egypt, China and Rome, and the land and tax records of many empires. The word statistics comes from the same root as state.
  • Averaging measurements. Astronomers in the 17th and 18th centuries increasingly combined repeated measurements by taking their mean. Adrien-Marie Legendre published the method of least squares in 1805, and Carl Friedrich Gauss developed it further; Theorem 6 is its simplest case. Pierre-Simon Laplace studied the median as a way of combining observations.
  • Quartiles and the median. Francis Galton popularised the median and quartiles in Britain in the late 19th century as simple, robust summaries.
  • Pictures of data. Florence Nightingale's diagrams of deaths in the Crimean War (1858) helped change hospital practice. Mary Eleanor Spear drew a “range bar” in 1952, and John Tukey introduced the box plot in his Exploratory Data Analysis (1977).
  • Global statistics. P. C. Mahalanobis founded the Indian Statistical Institute in Kolkata in 1931 and pioneered large national sample surveys. Today statistical offices across the world, and international bodies such as the United Nations Statistics Division, measure health, education and poverty with exactly the tools of this lesson.
Further reading

Further reading

  • David Freedman, Robert Pisani and Roger Purves, Statistics (1978; 4th edition 2007) — clear, honest, and full of real data.
  • David Spiegelhalter, The Art of Statistics (2019).
  • John W. Tukey, Exploratory Data Analysis (1977), where the box plot was introduced.
  • Darrell Huff, How to Lie with Statistics (1954), on misleading summaries and graphs.