stat — one number for the whole run
Use it when a row needs to know something about every row, including the ones after it: is this price above the average, what is the largest order in the file, how many rows carry a value at all.
This is the third and last axis of "a value that is not drawn but derived". accumulate=
totals a list inside one record. running totals a column as it goes, so
row 40 knows about rows 1–39. A statistic knows about all of them.
<tdc>
<env count="8" seed="basket" local="en">
<sequence name="Price"><gen type="number" value="10..200" decimals="2"/></sequence>
<sequence name="Average"><gen type="stat" of="Price" op="mean" decimals="2"/></sequence>
<sequence name="Verdict">
<gen if="Price > Average" type="text" value="above average"/>
<gen type="text" value="below average"/>
</sequence>
</env>
<block>
<line><data>${{Price}} ${{Average}} ${{Verdict}}</data></line>
</block>
</tdc>
156.83 122.52 above average 176.51 122.52 above average 135.17 122.52 above average 157.94 122.52 above average 50.71 122.52 below average 92.28 122.52 below average 50.37 122.52 below average 160.35 122.52 above average
"Is this row above average" cannot be asked any other way. The average is not knowable
until the last row exists, and a conditional <gen> is evaluated while the row is being
built — so the statistic has to be a column of its own, computed first.
The values come from a fixed seed, so they're reproducible, but exact strings can differ
between core versions. Treat them as examples of shape, not guarantees.
At a glance
| Attribute | Required | What it does |
|---|---|---|
of | yes | The column to summarise. Must be declared above this sequence |
op | yes | sum, mean, median, min, max, count or stddev |
decimals | no | Round the answer to N places, 0–10 |
A statistic draws nothing. It reads a column that already exists, consumes no randomness at all, and therefore adding one leaves every other column exactly where it was.
The seven statistics
<env count="6" seed="lab" local="en">
<sequence name="Reading"><gen type="number" value="1..100"/></sequence>
<sequence name="Total"><gen type="stat" of="Reading" op="sum"/></sequence>
<sequence name="Smallest"><gen type="stat" of="Reading" op="min"/></sequence>
<sequence name="Largest"><gen type="stat" of="Reading" op="max"/></sequence>
<sequence name="Middle"><gen type="stat" of="Reading" op="median"/></sequence>
<sequence name="Spread"><gen type="stat" of="Reading" op="stddev" decimals="3"/></sequence>
<sequence name="Rows"><gen type="stat" of="Reading" op="count"/></sequence>
</env>
60 sum=297 min=7 max=81 median=63 sd=27.415 n=6 81 sum=297 min=7 max=81 median=63 sd=27.415 n=6 66 sum=297 min=7 max=81 median=63 sd=27.415 n=6 7 sum=297 min=7 max=81 median=63 sd=27.415 n=6 17 sum=297 min=7 max=81 median=63 sd=27.415 n=6 66 sum=297 min=7 max=81 median=63 sd=27.415 n=6
Two of them are worth stating outright, because the alternatives are just as common and give different numbers:
stddevis the POPULATION standard deviation — divided by n, not by n−1. A generated column is the whole of what it describes, not a sample drawn from something larger, so n is the honest divisor. The same choice thestddev()expression function makes.medianon an even count is the average of the two middle values, so a column of2,4,4,4,5,5,7,9has median4.5.
count is how many rows carried a value — rows a parent
filter emptied are not counted, and take no part in any of the others either. That is the
same rule accumulate= and running follow, so a filtered column means one thing across
all three rather than three.
Exact decimals, and decimals=
sum, min and max are the last value of the corresponding running column. That is
not a shortcut — it is what keeps the two features from drifting: the arithmetic runs on
whole numbers scaled by the widest fraction in the column, never on floating point, so
19.99 + 0.01 + 0.01 is 20.01 in all five implementations rather than
20.009999999999998 in one of them. min and max return an element that already exists,
so a value that arrived as 007 stays 007.
mean, median and stddev are ratios and cannot be exact. Without decimals= they
print in full, digits and all, because a mean that quietly lost digits is worse than an
ugly one. With it they are rounded — and a half goes away from zero, the same rule
round() follows everywhere else in TDC, so 117.045 at two places is 117.05.
Declaration order
of= names a column, and it must be declared above the statistic (TDC240) — the same
rule parent and running follow, for the same
reason: the statistic is built out of a column that already exists.
A statistic that does not say what to summarise, or which statistic to take, is TDC262.
error[TDC262]: op="men" is not one of sum, mean, median, min, max, count, stddev --> lab.tdc:4:63 | 4 | <sequence name="Spread"><gen type="stat" of="Reading" op="men"/></sequence> | ^^^ | help: did you mean "mean"? note: One of: sum, mean, median, min, max, count, stddev.
Which engine runs it
The streaming engine refuses a statistic, by name, and the router sends the config to the in-memory one:
tdcv2: a statistic ("Average") is computed over every row of the run, including the ones after this one, so it cannot be computed one row at a time; the in-memory engine handles it (run without a forced streaming engine)You normally never see that message — the router picks the engine itself, and the refusal only surfaces when a config names a streaming engine and so has asked to be told.
What it costs is the same boundary running has, one
step stronger: a running total at least knows its answer by the time it reaches a row,
while a statistic does not know it until the run is over. The column it reads is held for
the run.
Everything else still streams. The limit is per config, not per project: a run without a statistic is untouched.
See also
running— the same column, accumulated as it goes rather than summarised at the endaccumulate=on a repeat list — the same idea inside one record, free on every engine- Expressions —
sum,mean,medianandstddevalso exist as functions, over a list inside one row rather than down a column