Numlex Documentation
Syntax Reference
Report issue

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). #food is 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\u0301 is 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 + 3 is 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.

  • N is a positive whole number. After the surrounding spaces or tabs, the line must read total 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.
  • N counts 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. subtotal and grand total are 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 following total or total last N starts empty.
  • Overflow and dynamic values are judged by the window. The quiet Invalid expression appears only when summing the selected lines overflows; an overflow above the window does not affect it. The row is dynamic (dependent on rand) only when a selected eligible contribution is.
  • Shadowing is shared: an active variable or constant named total suppresses 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. ⌘⇧T additionally requires at least two successful subtotals above.
  • Double-clicking an empty answer row (whitespace-only, or a strict <name> =) inserts subtotal through 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) → 3
  • median(1, 2, 3) → 2
  • median(1, 2, 3, 4) → 2.5
  • stdev(2, 4, 4, 4, 5, 5, 7, 9) → ≈ 2.13809 (sample, n−1)
  • rand(1, 6) → a uniform integer in 1…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.

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.

Found an issue in these docs? Report it on GitHub