Lesson 06 / 13
Choosing Descriptive Summaries
Three defensible summaries come out of the same 557 readings: the mean is 24.674 m³, the median 24.120 m³ and the 10% trimmed mean 24.581 m³, with a largest difference of 0.554 m³ among them. Bootstrap measures the margin of variation of all three and the widths come out as 1.552, 1.762 and 1.458 m³: the difference the choice moves is a third of the margins, and all three intervals contain the 25 m³ tariff step. When the same computation drops from the 557-reading group to the 142-reading group, the choice difference rises to 1.679 m³ and the margin width to 2.917 m³, and this time the decision cannot be read independently of the margin.
Published Updated
In this lesson
- Computes the mean, median and trimmed mean on the same dataset and writes how many cubic meters the choice moved the reported number
- Produces the margin of variation of each summary with bootstrap and reads the margin next to the choice difference
- Shows with numbers that the summary with the narrow margin is not the right summary by itself, and that the three summaries answer three different questions
Contents
The exploration phase did not produce a decision. Over five lessons the distributions were opened, the breakdowns were tried and the relationships were scanned, and what remained is a list of questions; none of the questions on the list has been tested yet. Every breakdown that was looked at during exploration also left a debt to testing, and that debt will be counted at the end of the topic.
The first step of testing is not, as one might think, choosing a test. Before it comes a step that is often skipped: with which summary the number to be tested will be written. When the “mean consumption” of the east region’s readings is asked for, there are at least three defensible answers, and the three give three different numbers from the same dataset. The measure of this lesson is how many cubic meters this choice moves the number in the report, and how much that movement amounts to next to the number’s own margin.
- AS1. A descriptive summary is a decision. For the same dataset the mean, the median and the trimmed mean give three different numbers; none of them is an approximation of another, and the three answer three different questions.
- AS2. Every summary has a margin of variation, and the margin is measured with bootstrap: the dataset is drawn from itself many times, at the same size and with replacement, and the summary is recomputed at every draw. The draw count is written down; the margin depends on that count too.
- AS3. A summary with a narrow margin is not the right summary by itself. Narrowness does not change what the summary answers; it only says how much that answer moves from sample to sample.
- AS4. The difference the choice moves is read next to the margin. An argument over a difference smaller than the margin is an argument over noise.
- AS5. The smaller the group, the larger the margin. Next to every reported summary is written
how many observations it rests on; the margin of a summary without an
ncannot be read.
Three Summaries, Three Numbers
# dataset.py — MODEL: the measurement network of K03-K04 is regenerated with the same seed. # A long-format reading table of 3,199 rows, 1,329 subscribers, three periods, five regions. import math SEED, RAW, M32 = 20260218, 1400, 0xFFFFFFFF PERIODS = ["2026-01", "2026-02", "2026-03"] REGION = [("north", 0.28, 21), ("south", 0.22, 17), ("east", 0.18, 26), ("west", 0.14, 14), ("central", 0.18, 23)] def rng(t): x = ((t ^ (t >> 16)) * 2246822507) & M32 x = ((x ^ (x >> 13)) * 3266489909) & M32 s = [(x ^ (x >> 16)) & M32] def next_value(): s[0] = (s[0] * 1664525 + 1013904223) & M32 return s[0] / 4294967296 return next_value def pick(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 _kept, _dropped = [], [] for i in range(RAW): r = rng(SEED + i) b = REGION[pick(r(), [x[1] for x in REGION])] pick(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]) pick(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) (_dropped if r() < 0.046 else _kept).append( {"subscriber_id": 10001 + i, "region": b[0], "base": b[2]}) VALID = {a["subscriber_id"] for a in _kept} READINGS = [] for a in _kept + _dropped: r = rng(SEED + 7000 + a["subscriber_id"]) for d in range(pick(r(), [0.05, 0.12, 0.21, 0.62])): t = 0.0 if r() < 0.038 else math.floor( a["base"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100 if a["subscriber_id"] not in VALID: r() continue READINGS.append({"subscriber_id": a["subscriber_id"], "region": a["region"], "period": PERIODS[d], "consumption_m3": t}) r() def mean(v): return sum(v) / len(v) def median(v): s = sorted(v) n = len(s) return s[n // 2] if n % 2 else (s[n // 2 - 1] + s[n // 2]) / 2 def trimmed(v, p=0.10): s = sorted(v) k = int(len(s) * p) return mean(s[k:len(s) - k]) east = [o["consumption_m3"] for o in READINGS if o["region"] == "east"] U = [mean(east), median(east), trimmed(east)] print(f"fictional dataset (seed {SEED}): {len(READINGS)} rows, {len(VALID)} subscribers") print(f"east region: {len(east)} readings, {sum(1 for x in east if x == 0.0)} zero readings") print(f"mean {U[0]:.3f} | median {U[1]:.3f} | trimmed(10%) {U[2]:.3f}") print(f"largest difference among the three {max(U) - min(U):.3f} m3")
fictional dataset (seed 20260218): 3199 rows, 1329 subscribers east region: 557 readings, 20 zero readings mean 24.674 | median 24.120 | trimmed(10%) 24.581 largest difference among the three 0.554 m3
The dataset is fictional and is regenerated with the same seed from the measurement network of K03–K04; the 3,199 rows and 1,329 subscribers are the same numbers the previous course left behind. The east region’s mean of 24.674 m³ also comes from there, and it is the number asked about at the opening of this course.
Now two more numbers stand next to it. The median is 24.120 m³: the value that stands in the middle when the readings are sorted, that is, the number below which half of the dataset stays. The 10% trimmed mean is 24.581 m³: the mean of the 447 values left after the smallest 55 and the largest 55 readings are removed. All three are correct, all three come from the same 557 readings, and the largest difference among them is 0.554 m³.
That the mean is larger than the median is a piece of information: the distribution is skewed to the right, that is, a few large readings pull the mean up. The twenty zero readings, on the other hand, work in the opposite direction and press the mean down. These two effects partly cancel each other inside the same number, and someone who looks only at the mean cannot see either of them.
Measuring the Margin
# margin.py — the margin of variation is measured with bootstrap: the dataset is drawn from itself # 1,000 times at the same size and with replacement, the summary is recomputed at every draw, # and the middle 95% of the 1,000 values is taken. DRAWS = 1000 def percentile(s, p): i = p * (len(s) - 1) lower = int(i) upper = min(lower + 1, len(s) - 1) return s[lower] + (s[upper] - s[lower]) * (i - lower) def margin(v, summary, seed, draws=DRAWS): rand = rng(seed) n = len(v) d = sorted(summary([v[int(rand() * n)] for _ in range(n)]) for _ in range(draws)) return percentile(d, 0.025), percentile(d, 0.975) E = [12, 10, 10, 10, 10] y = lambda h: "".join(str(v).rjust(E[k]) if k else str(v).ljust(E[0]) for k, v in enumerate(h)) SUMMARIES = (("mean", mean), ("median", median), ("trimmed", trimmed)) print(y(["summary", "number", "lower", "upper", "width"])) for name, f in SUMMARIES: a, b = margin(east, f, SEED + 101) print(y([name, round(f(east), 3), round(a, 3), round(b, 3), round(b - a, 3)])) print(f"\n{len(east)} observations, {DRAWS} draws, all three on the same draws") print("the 25 m3 tariff step stays inside all three intervals")
summary number lower upper width mean 24.674 23.889 25.441 1.552 median 24.12 23.248 25.01 1.762 trimmed 24.581 23.876 25.334 1.458 557 observations, 1000 draws, all three on the same draws the 25 m3 tariff step stays inside all three intervals
Bootstrap answers a single question: if another set of readings of the same size had come from the same network, what would this summary have produced. Since there is no other dataset at hand, the dataset itself is put in the place of the population, 557 values are drawn from it with replacement, the summary is recomputed, and this is repeated 1,000 times. The middle 95% of the resulting 1,000 values is the summary’s margin of variation.
The three summaries were computed on the same draws; the difference between them comes from the summary, not from the luck of the draw. The widths lie between 1.458 and 1.762 m³. The difference the choice moved, on the other hand, was 0.554 m³, that is, a third of even the narrowest margin. The argument over which of the three summaries to write is, in this dataset, an argument that stays inside the margin.
In the report this amounts to a single sentence: is the east region’s consumption below the 25 m³ tariff step. All three numbers are below it, but all three intervals contain 25. The claim of staying below the step cannot be defended without writing the margin, whichever summary is chosen. In a report that does write the margin, the claim softens by itself and takes its correct form: the three observed numbers are all below the step, but this data cannot rule out that another set of readings from the same network would exceed the step.
Which Summary Answers What
# choice.py — what the summary choice writes into the report: total volume and trimmed rows. k = int(len(east) * 0.10) print(f"mean x n = {mean(east) * len(east):.2f} m3 reported volume") print(f"median x n = {median(east) * len(east):.2f} m3 same product, different number") print(f"difference between the two volumes {(mean(east) - median(east)) * len(east):.2f} m3") print(f"trimmed mean leaves {2 * k} readings out; " f"{sum(1 for x in sorted(east)[:k] if x == 0.0)} of them are zero readings") nonzero = [x for x in east if x > 0.0] print(f"if zero readings are not counted at all the mean becomes {mean(nonzero):.3f} m3")
mean x n = 13743.34 m3 reported volume median x n = 13434.84 m3 same product, different number difference between the two volumes 308.50 m3 trimmed mean leaves 110 readings out; 20 of them are zero readings if zero readings are not counted at all the mean becomes 25.593 m3
That the three summaries answer three different questions is visible here. The mean carries the total of the dataset: multiplied by 557 it gives 13,743.34 m³, and this is the volume that passed through the network itself. The median gives 13,434.84 m³ in the same product, and this number is the total of nothing; the 308.50 m³ in between is the measure of the median not answering the volume question. When the volume to be billed is asked, the median is the wrong summary.
The trimmed mean answers the third question: a center that resists extreme readings. It does so by leaving 110 readings out, and 20 of the ones it leaves out are zero readings. This is the summary with the narrowest margin, but the source of the narrowness is not the dataset itself, it is that 20% of the dataset does not enter the computation. A narrow margin here is not an advantage, it is a sign of a narrowed coverage.
The last line shows what the coverage decision does by itself. If the zero readings are not counted at all, the mean becomes 25.593 m³ and exceeds the tariff step. What the zero readings are was dealt with in the cleaning lesson of M26/K03; no elimination is done here, it is only recorded that the coverage decision moved the summary by 0.919 m³ and that this movement is larger than the choice difference.
When the Group Shrinks
# small.py — the same three summaries and the same margin, recomputed on a small breakdown. sub = [o["consumption_m3"] for o in READINGS if o["region"] == "east" and o["period"] == "2026-03"] for name, v in (("east / all", east), ("east / 2026-03", sub)): s = [f(v) for _, f in SUMMARIES] g = [b - a for a, b in (margin(v, f, SEED + 101) for _, f in SUMMARIES)] print(f"{name}: n={len(v)}, choice difference {max(s) - min(s):.3f} m3, " f"margin width between {min(g):.3f} and {max(g):.3f} m3") print(f" mean {s[0]:.3f} | median {s[1]:.3f} | trimmed {s[2]:.3f}")
east / all: n=557, choice difference 0.554 m3, margin width between 1.458 and 1.762 m3
mean 24.674 | median 24.120 | trimmed 24.581
east / 2026-03: n=142, choice difference 1.679 m3, margin width between 2.527 and 2.917 m3
mean 22.394 | median 20.715 | trimmed 22.112
The same region, a single period: 142 readings. The choice difference rose from 0.554 m³ to 1.679 m³, that is, to three times. The widths of the margins grew too and settled in a band of 2.5–2.9 m³. The two increases are read together: in the small breakdown the summaries both move away from each other and the variation of each one grows.
The source of the divergence is visible. In this period the mean is 22.394 m³ and the median is 20.715 m³, and the range of 1.679 m³ in between comes from a few large readings pulling the mean in the single-period breakdown. The trimmed mean, at 22.112 m³, stays next to the mean, because the readings doing the pulling are more than the trimmed 10%.
For the report the result is this: in the 557-reading group the summary choice is a detail that stays
inside the margin, whereas in the 142-reading group the choice difference exceeds half of the margin
and the decision can no longer be read independently of the margin. In both, what must be written is
the same — next to the number, the n and the margin.
The Margin Is Also a Number
# draws.py — the margin comes from a run, not from a calculation; when the run count changes, the # bounds of the margin move too. In this course the upper limit is 1,000 draws. for c in (200, 500, 1000): a, b = margin(east, mean, SEED + 101, c) print(f"{c:5d} draws: {a:.3f} - {b:.3f}, width {b - a:.3f} m3")
200 draws: 23.955 - 25.406, width 1.451 m3 500 draws: 23.949 - 25.426, width 1.477 m3 1000 draws: 23.889 - 25.441, width 1.552 m3
The margin comes not from a formula but from a run, and when the run count changes the bounds of the margin move too. With two hundred draws the width is 1.451 m³, with a thousand draws it is 1.552 m³; the 0.101 m³ in between is the margin’s own uncertainty. The ends of the interval are the part that moves the most, because the ends rest on the outermost twenty-fifth values of the 1,000, and in that region there are few draws.
In this course the draw count does not exceed 1,000. The limit is not a convenience but a record: a reported margin is not complete without writing how many draws it came from. The same rule will hold for the permutation count in later lessons, and there the run count will directly determine the resolution of the margin.
Summary
- The same 557 readings give three defensible summaries: the mean is 24.674 m³, the median 24.120 m³, the 10% trimmed mean 24.581 m³; the largest difference the choice moves is 0.554 m³.
- Bootstrap puts the dataset in the place of the population, draws from it 1,000 times with replacement and measures the margin of each summary: the widths come out as 1.552, 1.762 and 1.458 m³, that is, three times the choice difference.
- The summary with the narrowest margin is the trimmed mean, but the narrowness comes from 110 readings not entering the computation; a narrow margin does not mean the right summary, it means the coverage narrowed.
- The three summaries answer three different questions: the mean carries the total and its product with 557 is 13,743.34 m³, while the median’s same product, 13,434.84 m³, corresponds to no volume.
- When the breakdown drops from 557 readings to 142, the choice difference rises to 1.679 m³ and the margin width to 2.917 m³; this is why the number of observations it rests on is written next to every summary.
Next Step
A single number was chosen and its margin was written, but a single number does not describe a distribution. The real question in a report is often the comparison of two groups, and by habit this comparison is made over five numbers: the minimum, the first quartile, the median, the third quartile, the maximum. The next lesson shows that these five numbers do not determine a distribution — two datasets whose five numbers are identical to each other are built, the bin counts are printed side by side, and it comes out in numbers that one has a single peak and the other has two. The notch interval of the median is again produced with bootstrap.
To keep your progress and take notes, Log in
My notes
Log in to take notes.