The blocks marked Run on this page can run in your browser: the first one downloads and starts a small machine, with no network. That needs JavaScript. Without it you read the output saved with the document.

No network

Python data#

Make a small dataset with only the Python standard library, summarize it, and then draw a chart that follows a number you can move. Run the blocks from the top.

Generate a dataset#

A week of made-up request counts for three services, from a fixed seed so everyone sees the same numbers.

python#dataset
import random

rng = random.Random(7)
services = ["api", "web", "worker"]
rows = []
for day in range(1, 8):
    for name in services:
        base = {"api": 120, "web": 200, "worker": 60}[name]
        rows.append((day, name, base + rng.randint(-25, 25)))

print(len(rows), "rows")
for row in rows[:6]:
    print(row)
Saved outputRan 2026-10-14
21 rows
(1, 'api', 115)
(1, 'web', 184)
(1, 'worker', 60)
(2, 'api', 136)
(2, 'web', 178)
(2, 'worker', 39)

Summarize it#

python#summary
import statistics

for name in services:
    counts = [n for (_, s, n) in rows if s == name]
    print(f"{name:7} min {min(counts):4}  max {max(counts):4}  "
          f"mean {statistics.mean(counts):7.2f}  stdev {statistics.stdev(counts):6.2f}")

busiest = max(rows, key=lambda r: r[2])
print("busiest:", busiest)
Saved outputRan 2026-10-14
api     min  108  max  136  mean  121.71  stdev  11.06
web     min  177  max  201  mean  182.71  stdev   8.40
worker  min   39  max   70  mean   53.29  stdev  13.66
busiest: (6, 'web', 201)
python#totals
from collections import defaultdict

by_day = defaultdict(int)
for day, _, n in rows:
    by_day[day] += n
for day in sorted(by_day):
    bar = "#" * (by_day[day] // 20)
    print(f"day {day}: {by_day[day]:4} {bar}")
Saved outputRan 2026-10-14
day 1:  359 #################
day 2:  353 #################
day 3:  368 ##################
day 4:  377 ##################
day 5:  325 ################
day 6:  362 ##################
day 7:  360 ##################

A live chart#

The number amplitude = 1 scales a daily temperature swing. Drag it or use the arrow keys when it has focus. The block below prints a table, and the chart under it draws that table again after every Run.

python#temperature
import math

rows_t = [
    [hour, round(18 + 5 * float(amplitude) * math.sin((hour - 9) / 24 * 2 * math.pi), 2)]
    for hour in range(0, 25, 2)
]
display({"application/vnd.wmn.table+json": {"columns": ["hour", "temperature"], "rows": rows_t}}, raw=True)
print(len(rows_t), "readings, amplitude", amplitude)
Saved outputRan 2026-10-14
hourtemperature
014.46
213.17
413.17
614.46
816.71
1019.29
1221.54
1422.83
1622.83
1821.54
2019.29
2216.71
2414.46
13 readings, amplitude 1
wmn:use#temperature-chartLine chart

Lines through a table's numeric columns, with a cursor that reads each point stdlib/charts@1.0.0

Show block text
source: temperature
x: hour
y: temperature
title: Temperature by hour

The chart is drawn from the table saved with the page when you open it. After a Run it is drawn from what the machine printed, and Reset under the chart goes back to the saved table.

Counts as a histogram#

python#latencies
rng2 = random.Random(3)
values = [round(rng2.lognormvariate(3.2, 0.5), 1) for _ in range(200)]
display({"application/vnd.wmn.table+json": {"columns": ["ms"], "rows": [[v] for v in values]}}, raw=True)
print("200 samples, median", sorted(values)[100])
Saved outputRan 2026-10-14
ms
15
18.5
27.5
18.7
42.6
28.2
58.9
26.5
28.7
42.2
20.6
44.5
15.6
11.7
16.5
49.8
28.7
24.8
18
52.2
29.2
54.9
31
47.8
20.1
22.4
11.4
27.2
32.5
20.6
26.8
15.8
27.4
42.4
22.9
33.3
28
52.8
19.9
17.8
57.7
25.6
20.6
27.7
30.1
31.1
48.3
16.6
27.6
20.7
18.6
18.6
30.7
19.9
34.5
15.3
29.6
19.5
40.8
35.4
16.4
44.8
18.8
25.3
16.8
51.2
36.6
15.7
34.7
19.6
21.8
7.9
39.3
89.1
32.8
18.6
19.1
27.3
55.2
16.4
32.9
7.3
28.4
22.4
20.9
28.9
54.7
14.4
16.1
21.6
12
19.5
17.1
27.6
16.5
11.9
33.1
15.8
18.6
36.3
48.7
22.3
24.3
19.6
23.5
27.1
12.9
14.5
44.6
16.8
84.4
52.2
38.8
10.7
36.5
60.9
61.7
39.2
30.2
14.1
33.8
18.7
14
35.1
20.3
19.7
12
49
31.6
48
33.4
23.6
36.6
51.4
20.6
17
13.2
19.9
13.5
14.8
27.1
23.8
15.8
12.5
23.5
20.8
45.7
23.2
40.9
23.7
11
36.6
17.1
9.9
18.6
30.4
18.6
30.3
13
22.1
13.5
46.6
25.6
13.8
44.9
64.3
14.3
48.5
10.8
25.4
24.3
43.9
26.3
34.4
28.6
14.6
29.6
31
20.2
39.6
33.8
17.7
24.7
35.1
19.3
28.7
10.8
15.5
59
17.6
18.6
72.1
26.9
22
25.9
17.4
45.8
27.3
30.8
44.6
200 samples, median 25.4
wmn:use#latency-histogramHistogram and CDF

How the values of one column are spread: bins, cumulative share, median and the P95 and P99 tails stdlib/charts@1.0.0

Show block text
source: latencies
column: ms
bins: 12
title: Response time