Tags, subtotals & statistics
Tagged rows, divider sections, subtotals, grand totals, tag aggregates, statistics, dynamic random values and the configurable footer statistic.
Tags and subtotals add a plain-text structure on top of the notebook. A trailing
#tag marks a row, an exact --- divider starts a new section, subtotal
sums the current section, grand total sums the successful subtotals, and tag
aggregates answer total of #tag and friends. Four functions —
count, median, stdev and rand — are included, and the floating
Total can show Sum, Average, Count or Median. Everything is strict: a
near-miss stays an ordinary row, and aggregates never re-consume derived rows.
Headings, tags and dividers
- A heading is a line starting exactly with
#(hash plus one space).#foodis not a heading — it is a tagged row. - A tag is a terminal atom:
#with no space, followed by an identifier that starts with a letter (letters, digits,_,-; up to 64 graphemes). Several tags may follow on one line; duplicates on the same line collapse (the first spelling wins). Case and NFC are normalized for lookup while the original spelling is preserved, so a decomposed#cafe\u0301is a valid tag and finds the same aggregate as#café. Tags never change a line’s answer and are not recognized mid-expression (2 #food + 3is arithmetic). Comments and headings never declare tags. - A divider is a line that is exactly
---(outer spaces allowed). It has no answer, never enters a sum, and starts a new tag section.----and longer dash runs are ordinary lines.
2 + 3 #math #cash → 5, tagged math and cash
5 #a
total of #a → 5
Sections, subtotals and grand totals
The legacy total command still works, and a # heading, an exact ---
divider or the nearest total now acts as its boundary. The feature set adds:
subtotal— sums the eligible rows since the last subtotal, heading or divider.subtotal + 15%/subtotal - 15%— the percentage applies to the raw subtotal (100→115).<name> = subtotal[ ± 15%]— a named form that writes the value into the environment as an ordinary scalar (sum = subtotal + 15%→115).grand total(and<name> = grand total) — sums only the successfully computed subtotal rows above it; a divider does not erase the list. A subtotal that failed or overflowed never counts.
total last N — a total over the last few lines
total sums every eligible row of its section. The bounded form
total last N sums only the eligible values found in the N immediately
preceding logical lines of that same section, and then resets the section
exactly like plain total.
Nis a positive whole number. After the surrounding spaces or tabs, the line must readtotal last N(case does not matter, and one or more spaces or tabs may separate the three parts).total last 0,total last -3,total last 3.5, a missing count, extra words or a number too large to represent are ordinary lines, not commands.Ncounts logical lines, not results. Blank lines, comments, prose, errors, money and unit rows, a bare answer token, and derived rows (subtotals, grand totals, tag aggregates) each occupy exactly one slot and contribute nothing. The command’s own line is never one of its slots.- The section boundary is unchanged. The window never looks above the
nearest
total/total last N,#heading or exact---divider.subtotalandgrand totalare not legacy boundaries, but their own rows still occupy slots. - Eligibility is unchanged. The same rules apply as for plain
total: finite unitless scalars, exact integers and named-scalar usage rows count; declarations, money, unit-bearing quantities, booleans, dates and errors do not. - A window larger than the section sums what is there (an empty section is
0), and the command resets the section afterwards — so a followingtotalortotal last Nstarts empty. - Overflow and dynamic values are judged by the window. The quiet
Invalid expressionappears only when summing the selected lines overflows; an overflow above the window does not affect it. The row is dynamic (dependent onrand) only when a selected eligible contribution is. - Shadowing is shared: an active variable or constant named
totalsuppresses both forms, exactly as before.
10
20
30
total last 2 → 30 (the blank line took a slot and added nothing)
40
50
total last 3 → 90 (only 40 and 50 were in the window)
total → 0 (the section was already reset)
grand total requires at least two successful subtotals; with fewer, the row
is the quiet generic error Invalid expression and nothing is guessed.
10
20
subtotal → 30
10
subtotal
20
subtotal
grand total → 30
A heading or divider resets a subtotal’s accumulator:
10
# Section
20
subtotal → 20
The legacy total row is not a subtotal boundary; it is excluded from the
subtotal as a derived row, while the legacy total’s own section still spans a
subtotal row.
Which rows are eligible
Eligible rows are finite dimensionless scalars (plain numbers, fractions,
percents, multipliers), exact integers, and rows that use named scalars.
Not eligible: declarations (x = …), bare single-token rows, money,
unit-bearing quantities, booleans/dates/times/geo values, errors, dividers and
every derived row. A token inside a genuine expression counts; a bare token-only
row does not.
x = 5
x + 5
$7
3 kg
true
50%
1/4
0xFF
2 + 3
subtotal → 270.75
Adding structural rows
- File → Add Subtotal (
⌘T) and File → Add Grand Total (⌘⇧T) insert the command into the caret line. They fire only when the target line is blank (whitespace-only) or a strict empty<name> =(trailing spaces allowed); anything else beeps and changes nothing.⌘⇧Tadditionally requires at least two successful subtotals above. - Double-clicking an empty answer row (whitespace-only, or a strict
<name> =) insertssubtotalthrough the same planner. Headings, comments, dividers, tag-only rows, prose and errors stay inert. - Convert to Normal Line in a subtotal/grand-total row’s context menu
replaces the command with a locale-retypeable full-precision numeric literal
of the displayed value; a named form keeps its
<name> =prefix, and line count and stable identifiers are unchanged.
Tag aggregates
total of #tag, average of #tag, count of #tag and median of #tag
(case-insensitive; exactly one tag per query) see only the eligible rows
tagged with that tag strictly above them in the current section — a divider
closes the section. A row with several tags joins each tag once. An empty set
gives 0 for total/count and a quiet error for average/median.
Derived aggregate rows never feed later aggregates, the subtotal or the footer.
Statistics functions and natural phrases
count, median and sample stdev (n−1) are part of the 27 built-in
functions, and rand supplies inclusive random integers. Every function
follows the shared call rules: case-insensitive names, full expression
arguments, strict arity and domain errors.
count(1, 2, 3)→ 3median(1, 2, 3)→ 2median(1, 2, 3, 4)→ 2.5stdev(2, 4, 4, 4, 5, 5, 7, 9)→ ≈ 2.13809 (sample, n−1)rand(1, 6)→ a uniform integer in1…6
Natural phrases cover the same ground: median of 1, 2, 3 → 2,
count of 1, 2, 3, 4 → 4, standard deviation of 2, 4, 4, 4, 5, 5, 7, 9 and
random number between X and Y. A list uses the active argument separator
(, in point formats, ; in decimal-comma formats), so
median of 1,5; 2,5; 3,5 → 2.5 in a comma-decimal setup. Results follow the
sheet’s decimal places. The median and average are overflow-safe, so finite
extreme values cannot produce infinity.
Dynamic random values
A row becomes dynamic when rand actually ran (spacing and case before (
do not matter) or when it reads a dynamic name. An untaken if branch,
comments and invalid text do not become dynamic. The dependence propagates
through named values, subtotal, grand total and tag aggregates; the row
kind is preserved (a subtotal stays a subtotal).
A dynamic value stays stable while you scroll, hover, change a format or use
the footer menu — but it is never tokenizable and never joins the Previous
Answer chain. ⌘R (Recalculate Dynamic Values) and a semantic edit give it a
fresh value; PDF export freezes the current one.
Footer statistic
Right-click the floating Total to choose Sum, Average, Count or
Median from one native checked menu. The choice is an app-global, persisted
setting (never part of .nlx) and changes only the footer — never the sheet,
caret, selection, IME, scroll position or editor. Count shows an exact integer;
the other statistics use the global number presentation. The panel uses the
same eligible rows as the totals above: derived aggregate rows
(inline totals, subtotals, grand totals, tag aggregates, dividers) never
contribute, and the default Sum matches the previous Total. An unknown
value falls back to Sum.
Tokens, export and totals interaction
- A finite tag-aggregate or subtotal row is an ordinary number for Answer Tokens; the same result is never re-consumed by a later aggregate, subtotal or the footer.
- Per-answer rounding and number-format overrides never change the footer statistic; it follows the global notation and regional settings.
- PDF export and printing keep derived rows marked as derived, so the exported page never double-counts them, and export freezes the current random value.
Related
- Built-in functions — the full function list and strict call rules.
- Sheet language — line forms and the legacy
totalcommand. - Export & print — how derived rows and the footer statistic appear in a PDF.