Перейти к основному содержимому

Генератор formula

Используйте, когда колонка не разыгрывается, а вычисляется — из других колонок той же строки. Вес, который следует за ростом. ИМТ из обоих. Цена, умноженная на количество. Остаток, который нужно напечатать, а не проверить.

<gen type="formula" expr="0.75 * Height - 58 + 6 * Z" decimals="1"/>

expr= — это тот же язык, что и if=: те же операторы, те же функции, те же имена для тех же колонок. Разница одна — куда девается ответ: if= съедает его как «да/нет», а формула оставляет значением своей колонки.

АтрибутЧто задаёт
exprОбязательный. Арифметика, которой эта колонка является
decimalsЗнаков после точки, 0..10. Без него значение печатается целиком

Колонка, которая следует за другой

Настоящие данные — не набор независимых колонок: вес идёт за ростом, доход за образованием, площадь за ценой. Формула — способ это сказать:

<sequence name="Height"><gen type="number" distribution="normal" mean="170" sd="10" decimals="1"/></sequence>
<sequence name="Z"> <gen type="number" distribution="normal" mean="0" sd="1" decimals="4"/></sequence>

<sequence name="Weight"><gen type="formula" expr="0.75 * Height - 58 + 6 * Z" decimals="1"/></sequence>
<sequence name="BMI"> <gen type="formula" expr="Weight / pow(Height / 100, 2)" decimals="1"/></sequence>
<sequence name="Label"> <gen type="formula" expr="BMI > 25 ? over : normal"/></sequence>
./run clinic.tdc
152.3,62.1,26.8,over
187.3,73.4,20.9,normal
172.9,69.6,23.3,normal
164.9,59.8,22.0,normal
159.3,60.9,24.0,normal
157.4,63.9,25.8,over

Height разыгран, Z — шум, из-за которого связь не превращается в прямую линию, остальные три вычислены. Z печатать не обязательно: не упоминайте колонку в <block>, и она останется в расчёте, не попав в файл. В конфиге для data science таких обычно несколько.

Последняя строка стоит отдельного внимания: тернарник заставляет формулу выдавать метку, а не только число — так у обучающего набора появляется целевая колонка.

Деление и остаток, который можно напечатать

Деление здесь настоящее и было таким всегда — это единственный оператор, который не может остаться целым, потому что целые числа делением не замыкаются:

<sequence name="N"> <gen type="number" value="1..20"/></sequence>
<sequence name="Half"><gen type="formula" expr="N / 2"/></sequence>
<sequence name="Rem"> <gen type="formula" expr="N % 3"/></sequence>
<sequence name="Row"> <gen type="formula" expr="_count"/></sequence>
./run numbers.tdc
n=15 half=7.5 rem=0 row=1
n=9  half=4.5 rem=0 row=2
n=9  half=4.5 rem=0 row=3
n=5  half=2.5 rem=2 row=4

_count и любая другая встроенная величина читаются, так что тренд руками — это expr="100 + 0.05 * _count".

Сравнение печатает true / false, а не 1 / 0

expr="BMI > 25" даёт слово true — так же, как _last, _first и любая колонка anomaly_flag: одно написание для флага во всём движке.

Обучающему набору обычно нужна другая форма, и тернарник рядом:

<gen type="formula" expr="BMI > 25 ? 1 : 0"/>

Целые остаются целыми

Операнд, который ЯВЛЯЕТСЯ целым числом, так и несётся целым и становится дробным, только когда об этом попросят. Поэтому формула точна везде, где точны её входы — 1000000 * 1000000 даёт верный ответ, а не округлённый, — и становится приблизительной только тогда, когда конфиг попросил чего-то неточного. Правила те же, что и во всём языке выражений, и выписаны в Выражениях.

Две дроби редко равны

0.1 + 0.2 == 0.3ложь, здесь и в любом другом языке, потому что у 0.1 нет точной двоичной формы. Это честная арифметика IEEE, а не причуда движка, — но означает, что ветка, написанная как if="A + B == 0.3", может не сработать никогда. Сравнивайте через < и > либо сначала округляйте обе стороны.

Пустой источник даёт пустой ответ

Ячейка, которую опустошил parent= или missing=, — это не ноль, и формула, читающая её, выдаёт пустоту, а не выдуманное число:

<sequence name="H" parent="G.M"><gen type="number" value="170..190"/></sequence>
<sequence name="W"><gen type="formula" expr="H * 2"/></sequence>
./run people.tdc
F,,
F,,
M,170,340
M,172,344

Строки, где у H значения нет, оставляют пустым и W. Это то же правило, которому следуют накопление и статистика, пропуская опустошённую ячейку, — только с другой стороны. Оно же делает формулу безопасной поверх колонки с missing=: пропуски остаются пропусками, а не превращаются в арифметику.

Обёртки, которых формула не берёт

mask=, case=, missing=, missing_as=, repeat=, anomaly= и anomaly_factor= получают отказ TDC015, а не принимаются и игнорируются. Формула разрешается до того, как заработает слой форматирования, — то же место, что у running и stat.

Ответ существует шагом позже и он лучше, потому что работает там, где значение ПЕЧАТАЕТСЯ:

<data>${{Weight|mask:x}}</data>

Какой слой: formula или <compute>

Оба вычисляют значение, и разделение тут не дело вкуса:

formulaматематика — дроби, деление, функции, любая производная величина
<compute>контрольные цифры и работа с текстом — мод-11, Луна, дополнение, вырезание

<compute> намеренно работает только с целыми, потому что контрольной цифре нужно именно это. Написать формулу в его дереве тегов можно, и это мучительно: (x - lo) / (hi - lo) там — полэкрана вложенности, а здесь тринадцать символов.

Что отказывает check

Всё, что нужно формуле, известно из конфига, поэтому ничего не ждёт прогона:

  • нет expr=TDC294. Формула И ЕСТЬ своё выражение.
  • expr=, который не разбираетсяTDC294, с указанием на место.
  • имя, не являющееся колонкой, объявленной вышеTDC240, тот же код, что у running и stat на то же правило, с подсказкой did you mean, если имя похоже на настоящее. Это самое важное: опечатка в if= — просто слово, и ветка тихо перестаёт срабатывать, а опечатка в формуле доходит до арифметики.

Ещё два отказа выдаёт прогон, потому что они зависят от значений, а не от конфига: арифметика над текстовой колонкой (откуда иначе взялся бы NaN, а файл, полный NaN, о котором никого не предупредили, хуже остановленного прогона) и деление на ноль.

Подробности

  • Читает свою строку. Строка i вычисляется из строки i и ничего больше, поэтому формула не тратит случайность — добавление её оставляет все остальные колонки на месте.

  • Порядок объявления. Формула строится из колонок, которые уже есть, поэтому каждое имя в expr= должно принадлежать последовательности, объявленной выше.

  • Она потоковая. Чтение только своей строки — ровно то, на чём построен потоковый движок, поэтому формула работает там как любая разыгранная колонка, и память не растёт вместе с числом строк. Измерено на одном конфиге: 1 млн строк — 2.1 с, 5 млн — 3.9 с, 20 млн — 9.5 с и файл на 291 МБ, при этом пик памяти вырос в 1.3 раза, пока число строк выросло в 20. Движок в памяти на тех же 5 млн занял 12.5 с и больше памяти, а до 20 млн не дотягивает вовсе.

    Этим формула и отличается от накопления и статистики: тем двум нужны чужие строки — все предыдущие и вообще все, — поэтому они остаются в памяти по своему смыслу, а не по недоделке.

Смотрите также

  • Выражения — операторы, функции и правила целых чисел.
  • Накопление — когда ответу нужны строки до этой.
  • Статистика — когда нужен весь прогон.
  • Number — разыгранные колонки, которые формула читает.