stat — одно число на весь прогон
Когда нужен: строке надо знать что-то про все строки, включая те, что идут после неё: выше ли эта цена средней, какой самый крупный заказ в файле, у скольких строк вообще есть значение.
Это третья и последняя ось «значения, которое не разыгрывается, а выводится».
accumulate= считает итог по списку внутри одной записи. running считает
итог по колонке на ходу, поэтому сороковая строка знает про строки с первой по тридцать
девятую. Статистика знает про все.
<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
«Выше ли эта строка среднего» иначе не спросить. Среднее неизвестно, пока не появилась
последняя строка, а условный <gen> вычисляется в момент сборки строки — поэтому
статистика должна быть отдельной колонкой, посчитанной заранее.
Значения получены с фиксированным seed, то есть воспроизводимы, но точные строки могут
отличаться между версиями ядра. Смотрите на них как на пример формы, а не как на
гарантию.
Коротко
| Атрибут | Обязателен | Что делает |
|---|---|---|
of | да | Колонка, по которой считать. Должна быть объявлена выше этой |
op | да | sum, mean, median, min, max, count или stddev |
decimals | нет | Округлить ответ до N знаков, от 0 до 10 |
Статистика ничего не разыгрывает. Она читает уже существующую колонку, не тратит ни капли случайности — поэтому её добавление не сдвигает ни одну другую колонку.
Семь статистик
<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
Две из них стоит проговорить вслух — потому что альтернативы встречаются не реже и дают другие числа:
stddev— это стандартное отклонение по ГЕНЕРАЛЬНОЙ совокупности, деление на n, а не на n−1. Сгенерированная колонка — это всё описываемое целиком, а не выборка из чего-то большего, поэтому честный делитель — n. Тот же выбор делает функцияstddev()в выражениях.medianпри чётном количестве — это среднее двух средних значений, поэтому у колонки2,4,4,4,5,5,7,9медиана4.5.
count — это сколько строк несли значение: строки, опустошённые фильтром
parent, не считаются и не участвуют ни в одной
из остальных статистик. То же правило соблюдают accumulate= и running, поэтому
отфильтрованная колонка означает во всех трёх одно и то же, а не три разных вещи.
Точные копейки и decimals=
sum, min и max — это последнее значение соответствующей нарастающей колонки. Это
не срезанный угол, а то, что не даёт двум возможностям разойтись: арифметика идёт по целым
числам, отмасштабированным по самой длинной дробной части в колонке, и никогда по
плавающей точке, поэтому 19.99 + 0.01 + 0.01 — это 20.01 во всех пяти реализациях, а не
20.009999999999998 в одной из них. min и max возвращают уже существующий элемент,
поэтому значение, пришедшее как 007, так и остаётся 007.
mean, median и stddev — отношения, и точными быть не могут. Без decimals= они
печатаются целиком, со всеми знаками: среднее, которое тихо потеряло цифры, хуже, чем
некрасивое. С decimals= они округляются — и половина уходит от нуля, по тому же
правилу, что и round() везде в TDC, поэтому 117.045 до двух знаков — это 117.05.
Порядок объявления
of= называет колонку, и она должна быть объявлена выше статистики (TDC240) — то же
правило, что у parent и у running, и по той
же причине: статистика строится из уже существующей колонки.
Статистика, которая не говорит, что считать или какую именно статистику брать, — это
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.
На каком движке это считается
Потоковый движок отказывает статистике по имени, и роутер уводит конфиг на движок памяти:
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)Обычно этого сообщения никто не видит — движок выбирает роутер, и отказ всплывает только тогда, когда конфиг сам назвал потоковый движок и тем самым попросил себе объяснение.
Цена — та же граница, что у running, только
на шаг жёстче: нарастающий итог хотя бы знает свой ответ к моменту, когда доходит до
строки, а статистика не знает его до конца прогона. Колонка, которую она читает, держится
в памяти весь прогон.
Всё остальное по-прежнему течёт потоком. Ограничение действует на конфиг, а не на проект: прогон без статистики его не касается.
См. также
running— та же колонка, но накопленная на ходу, а не подытоженная в концеaccumulate=на repeat-списке — та же идея внутри одной записи, бесплатно на любом движке- Выражения —
sum,mean,medianиstddevесть и как функции, но по списку внутри строки, а не по колонке