Describe a dataset's spread with five landmarks: minimum, Q1, median, Q3, and maximum. With n=9, percentile positions (n−1)×p/100 for p∈{0,25,50,75, 100} land on exact integers (0, 2, 4, 6, 8) — no interpolation. Loop over the five percents, compute each position, read sv[pos]. With numpy, np.percentile(x, [0,25,50,75,100]) gives the same five values via linear interpolation (exact at integer positions).

By hand

n=9 gives positions 0, 2, 4, 6, 8 for p=0,25,50,75,100 — all integers. Loop over percents=[0,25,50,75,100], compute pos = int((n-1)*p/100), append sv[pos]. Unpack: min=1, Q1=3, median=5, Q3=7, max=9.

naive.py
Replay: real traced execution (multi-file project)
values = [4, 7, 2, 9, 1, 8, 5, 6, 3]
sv = sorted(values)
n = len(sv)
percents = [0, 25, 50, 75, 100]
summary = []
for p in percents:
    pos = int((n - 1) * p / 100)
    summary.append(sv[pos])
lo, q1, median, q3, hi = summary
print('RESULT:', (lo, q1, median, q3, hi))
  1. values ← [4, 7, 2, 9, 1, 8, 5, 6, 3]

    1values = [4, 7, 2, 9, 1, 8, 5, 6, 3]2sv = sorted(values)
    values this step[4, 7, 2, 9, 1, 8, 5, 6, 3]values
  2. sv ← [1, 2, 3, 4, 5, 6, 7, 8, 9]

    1values = [4, 7, 2, 9, 1, 8, 5, 6, 3]2sv = sorted(values)3n = len(sv)
    values this step[1, 2, 3, 4, 5, 6, 7, 8, 9]sv
  3. n ← 9

    2sv = sorted(values)3n = len(sv)4percents = [0, 25, 50, 75, 100]
    values this step9n
  4. percents ← [0, 25, 50, 75, 100]

    3n = len(sv)4percents = [0, 25, 50, 75, 100]5summary = []
    values this step[0, 25, 50, 75, 100]percents
  5. summary ← []

    4percents = [0, 25, 50, 75, 100]5summary = []6for p in percents:
    values this step[]summary
  6. p ← 0

    5summary = []6for p in percents:7    pos = int((n - 1) * p / 100)
    values this step0p
  7. pos ← 0

    6for p in percents:7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])
    values this step0pos
  8. summary ← [1]

    7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])9lo, q1, median, q3, hi = summary
    values this step[] [1]summary
  9. p ← 25

    5summary = []6for p in percents:7    pos = int((n - 1) * p / 100)
    values this step0 25p
  10. pos ← 2

    6for p in percents:7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])
    values this step0 2pos
  11. summary ← [1, 3]

    7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])9lo, q1, median, q3, hi = summary
    values this step[1] [1, 3]summary
  12. p ← 50

    5summary = []6for p in percents:7    pos = int((n - 1) * p / 100)
    values this step25 50p
  13. pos ← 4

    6for p in percents:7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])
    values this step2 4pos
  14. summary ← [1, 3, 5]

    7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])9lo, q1, median, q3, hi = summary
    values this step[1, 3] [1, 3, 5]summary
  15. p ← 75

    5summary = []6for p in percents:7    pos = int((n - 1) * p / 100)
    values this step50 75p
  16. pos ← 6

    6for p in percents:7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])
    values this step4 6pos
  17. summary ← [1, 3, 5, 7]

    7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])9lo, q1, median, q3, hi = summary
    values this step[1, 3, 5] [1, 3, 5, 7]summary
  18. p ← 100

    5summary = []6for p in percents:7    pos = int((n - 1) * p / 100)
    values this step75 100p
  19. pos ← 8

    6for p in percents:7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])
    values this step6 8pos
  20. summary ← [1, 3, 5, 7, 9]

    7    pos = int((n - 1) * p / 100)8    summary.append(sv[pos])9lo, q1, median, q3, hi = summary
    values this step[1, 3, 5, 7] [1, 3, 5, 7, 9]summary
  21. for p in percents:

    5summary = []6for p in percents:7    pos = int((n - 1) * p / 100)
  22. hi ← 9, lo ← 1, median ← 5, q1 ← 3, q3 ← 7

    8    summary.append(sv[pos])9lo, q1, median, q3, hi = summary10print('RESULT:', (lo, q1, median, q3, hi))
    values this step9hi1lo5median3q17q3
  23. stdout ← RESULT: (1, 3, 5, 7, 9)

    9lo, q1, median, q3, hi = summary10print('RESULT:', (lo, q1, median, q3, hi))
    values this stepRESULT: (1, 3, 5, 7, 9)stdout

With the library

np.percentile(x, [0,25,50,75,100]) extends the same linear-interpolation method to all five percentiles. Percentile 0 returns the minimum and 100 returns the maximum — no separate min()/max() calls needed. Values are float; cast with int() to match.

library.py
import numpy as np
from dalib.display import set_display
set_display()

x = [4, 7, 2, 9, 1, 8, 5, 6, 3]
lo, q1, median, q3, hi = np.percentile(x, [0, 25, 50, 75, 100])
print('min:', int(lo))
print('Q1:', int(q1))
print('median:', int(median))
print('Q3:', int(q3))
print('max:', int(hi))
print('RESULT:', (int(lo), int(q1), int(median), int(q3), int(hi)))
min: 1
Q1: 3
median: 5
Q3: 7
max: 9
RESULT: (1, 3, 5, 7, 9)

Implementation notes

  • The five-number summary is the data behind a box-and-whisker plot: the box spans Q1–Q3, the line inside is the median, and the whiskers extend to min and max (before outlier clipping).
  • np.percentile with p=0 and p=100 is equivalent to min() and max(); calling them through percentile keeps the loop uniform and avoids two extra passes.
  • Cross-reference: median-and-quartiles (this chapter) for the quartile position derivation; interquartile-range (this chapter) for the Q3−Q1 spread derived from this summary.