Блок <mix>
Когда использовать — когда доли в столбце не равны и каждая доля больше одного слова. Настоящие данные перекошены: большинство заказов — «оплачено», а «отменено» — единицы; большинство аккаунтов бесплатные, премиум — редкость. Нужен столбец, где варианты идут в фиксированной пропорции — не поровну и не случайным шумом.
<mix> — это распределение: именованный источник, который раскладывает свои
варианты — ветки <case> — по строкам в точных
процентах. По сути это <sequence>, значения
которой распределены по percent, только каждая ветка может быть целым набором
литералов и генераторов, а не одним простым значением. Раскладка детерминирована
для заданного seed.
Примеры вывода ниже иллюстративны — конкретные значения, которые даёт тот или иной
seed, могут меняться между версиями ядра, но количества, которые гарантирует
маска percent, — никогда.
- проценты, написанные в конфиге
- доля, которая получилась на самом деле
Именованное распределение
<mix> стоит прямо в <env>, рядом с
<sequence> — обёртка не нужна, достаточно задать name. К значению обращаются
через ${{Имя}}, как к любому другому именованному источнику:
<env count="100" seed="demo" inject="${{%}}">
<mix name="Code" percent="25,70">
<case><gen type="text" value="A"/></case>
<case><gen type="text" value="B"/></case>
<case><gen type="text" value="C"/></case>
</mix>
</env>
<block>
<line><data>${{Code}}</data></line>
</block>
Первые строки ничего не говорят — пропорции видны только на всей выборке:
A B A B B A
Посчитаем все 100 строк — раскрой точный:
A 25 B 70 C 5
Ровно 25 / 70 / 5. Маска задаёт первые две доли; третья ветка получает
остаток, 100 − 25 − 70 = 5. Это не «примерно 25 %», а точная раскладка
методом Хэмилтона (наибольших остатков) — тем же, что управляет
percent у генератора text.
Поменяли проценты — поменялись доли
Тот же набор веток A/B/C, но percent="60,30":
<mix name="Code" percent="60,30">
<case><gen type="text" value="A"/></case>
<case><gen type="text" value="B"/></case>
<case><gen type="text" value="C"/></case>
</mix>
A 60 B 30 C 10
60 / 30, а остаток 10 уходит третьей ветке. Одно число в маске перекраивает
весь столбец.
Когда нужен <mix>, а когда достаточно text
Для простого распределения готовых строк <mix> избыточен: генератор
text уже раскладывает точные доли через percent.
<sequence name="Gender">
<gen type="text" value="Мужчина,Женщина" percent="50,50"/>
</sequence>
Мужчина 10 Женщина 10
<mix> оправдывает себя только тогда, когда ветки составные — когда каждый
вариант собирается из своей смеси литерального текста и генераторов. Именно к
этому случаю подводят следующие разделы.
Атрибуты
| Атрибут | Обязательный | Что делает |
|---|---|---|
name | да | Имя для интерполяции через ${{Имя}} |
percent | нет | Доля каждого <case>; опустите — получите равномерное деление |
parent | нет | Родительская последовательность — доли считаются внутри её подмножества |
flag | нет | Добавляет колонку-ответ, отмечающую ветку-выброс (см. ниже) |
comment | нет | Свободная заметка для автора конфига; в вывод не попадает |
В <mix> должна быть хотя бы одна <case> — одна ветка.
Всё остальное необязательно.
percent — необязательный, и короткие маски
Опустите percent полностью — и ветки поделятся равномерно. Три кейса на 99
строк дают по 33 в каждом:
<mix name="Bucket">
<case><gen type="text" value="low"/></case>
<case><gen type="text" value="mid"/></case>
<case><gen type="text" value="high"/></case>
</mix>
low 33 mid 33 high 33
Если маску всё-таки задаёте, она подчиняется той же грамматике, что и
percent у text: число фиксирует долю своей ветки, а
каждая пустая позиция — голая запятая внутри маски ("25,,70") или хвостовая
запятая ("25,70,") — делит остаток от 100 поровну. Оба примера в начале страницы
уже опираются на это правило: percent="25,70" оставляет третьей ветке остаток.
parent — распределение внутри подмножества
Задайте <mix> атрибут parent — и проценты считаются против
отфильтрованного подмножества строк, а не всего count. Это то же правило,
что управляет зависимой <sequence>. Здесь
платные аккаунты получают разбивку по уровням, а бесплатные оставляют столбец
пустым:
<sequence name="Segment">
<gen type="text" value="Free,Paid" percent="70,30"/>
</sequence>
<mix name="Tier" parent="Segment.Paid" percent="60,30">
<case><gen type="text" value="Silver"/></case>
<case><gen type="text" value="Gold"/></case>
<case><gen type="text" value="Platinum"/></case>
</mix>
При count="100" строк Paid получается 30. Маска 60 / 30 применяется
к этим 30 строкам, поэтому Tier делится 18 / 9 / 3:
Silver 18 Gold 9 Platinum 3
18 + 9 + 3 = 30 — всё платное подмножество, а не процент от общей сотни. Это
сердце иерархической модели; полный разбор, с вложенными уровнями, — в статье
Иерархические зависимости.
<mix> вкладываются друг в друга
Внутри <case> может стоять вложенный <mix>, и вложенное деление считается
против строк, выбравших внешнюю ветку — то же правило подмножества, на уровень
глубже. Здесь треть всех строк — error, и внутри них внутренний <mix> градуирует
тяжесть:
<mix name="Status" percent="34,33">
<case>
<gen type="text" value="ok"/>
</case>
<case>
<gen type="text" value="warn"/>
</case>
<case>
<mix percent="70,20">
<case><data>error/minor</data></case>
<case><data>error/major</data></case>
<case><data>error/fatal</data></case>
</mix>
</case>
</mix>
При count="100" внешнее деление — 34 ok / 33 warn / 33 error. Внутренняя маска
70 / 20 затем делит эти 33 строки error на 23 / 7 / 3:
ok 34 warn 33 error/minor 23 error/major 7 error/fatal 3
23 + 7 + 3 = 33 — ровно подмножество ошибок.
То же верно уровнем выше: <mix>, записанный внутри ветки
<switch>, берёт квоту по строкам, которые эта
ветка совпала, а не по всему прогону.
Составные ветки
Вот что даёт <mix> и чего не даст голый список строк: <case> может собрать своё
значение из нескольких кусков — литерального фрагмента
<data>, одного или нескольких
генераторов и вложенных <mix>. В этом контексте
<data> — просто литеральный кусок значения («клей»), а не форматирование
вывода; о форматировании см. Маски и регистр.
<mix name="Charge" percent="10,12,34,">
<case><data>возврат: </data><gen type="number" value="1..10"/></case>
<case><data>чарджбэк: </data><gen type="number" value="11..20"/></case>
<case><gen type="number" value="21..40"/></case>
<case><gen type="number" value="41..100"/><data> (помечено)</data></case>
</mix>
36 возврат: 4 чарджбэк: 18 66 (помечено) возврат: 8 возврат: 1 73 (помечено) 82 (помечено)
Каждая ветка собрала свою форму: возврат: 4 — это литерал возврат: плюс
number в диапазоне 1..10; 66 (помечено) — число из
41..100, за которым идёт литерал (помечено); третья ветка — голое число без
всякой обёртки. Свернём 100 строк по веткам — доли по-прежнему точны:
возврат 10 чарджбэк 12 чистое 34 помечено 44
10 / 12 / 34 / 44. У последней ветки нет собственного процента — хвостовая
запятая в percent="10,12,34," оставляет её открытой, поэтому она берёт остаток,
44.
Распределение генерирует — оно не форматирует
<mix> порождает данные, поэтому живёт только в <env>. Поставить его в блок
вывода нельзя: <mix> прямо в <line> отклоняется ещё до запуска с ошибкой
TDC132, потому что блок вывода отвечает
только за раскладку. Объявите <mix name="…"> в <env> и подставьте ${{Имя}}
там, где нужно значение.
Если нужен выбор не по проценту, его покрывают два соседа:
<switch>выбирает по ключу — таблица соответствий, вродестрана → валюта.<sequence>с условными ветками<gen if="…">выбирает по произвольному условию — побеждает первая истинная ветка.
Пометка выбросов через flag
Ветку можно объявить аномальной — <case anomaly="true"> — и попросить у mix
колонку-ответ через flag. В результате получается набор данных, на котором можно
проверить детектор аномалий: и выбросы есть, и соседний столбец записывает,
на какие именно строки они пришлись.
<mix name="Temp" percent="75,25" flag="Bad">
<case><gen type="number" value="20..24"/></case>
<case anomaly="true"><gen type="number" value="90..99"/></case>
</mix>
${{Bad}} равно true ровно на тех строках, что пришли из помеченной ветки —
на 25 % строк, — и false на всех остальных. Метка выводится из того же
решения, которым выбиралась ветка, поэтому разойтись со значением она не может.
anomaly="true" — всего лишь ярлык: сам выброс — это то, что выдаёт генератор
ветки, поэтому вы полностью управляете тем, как он выглядит. Это лишь уголок более
широкой темы — инъекция выбросов и колонка flag получают полный разбор в
руководстве по аномалиям.
Дальше
- Генератор text — точные доли
percentдля простого списка вариантов, когда ветки — отдельные слова. - Последовательности — именованный источник, рядом с
которым стоит
<mix>, и модельparent, которую они делят. - Иерархические зависимости — полная
история про
parent, со вложенными процентами на нескольких уровнях.