Аномалии и выбросы — anomaly
Когда пригодится — когда вы готовите тестовые данные для детектора аномалий,
для алерта мониторинга или для модели, устойчивой к выбросам, — для всего, что
должно замечать значения далеко за пределами нормального диапазона. В реальных
данных такие всегда есть: залипший датчик, опечатка при вводе, подозрительная
транзакция. Атрибут anomaly подмешивает эти всплески специально, с той
частотой, которую вы зададите, — чтобы вашему конвейеру было на что реагировать.
Есть два способа, и эта страница описывает оба:
| Способ | Как выглядит выброс | Колонка-ответ |
|---|---|---|
anomaly="p" на <gen>, выдающем числа | базовое значение × коэффициент | anomaly_flag="Имя" |
anomaly="true" на <case> внутри <mix> | то, что генерирует эта ветка | flag="Имя" |
Первый способ — когда «слишком большое» число вас как выброс устраивает. Второй — когда выбросу нужна своя форма: другой диапазон, мусорная строка, редкое, но допустимое событие.
Числа ниже — то, что даёт типичный прогон. Конкретные значения могут отличаться от
версии ядра и seed. Неизменной остаётся структура: какие строки всплеснут,
решает номер строки, поэтому колонка-ответ всегда согласована со значением.
- Aчистый ряд
- Bс anomaly= — отмеченные точки это подмешанные выбросы
- Cс missing= — засечки внизу это строки, где значения нет
Как включить
Поставьте anomaly="p" на генератор, выдающий числа, где p — доля значений (от 0 до 1),
которые станут выбросами. Размер каждого всплеска задаёт anomaly_factor (по
умолчанию 10):
<gen type="number" distribution="normal" mean="50" sd="3" anomaly="0.15" anomaly_factor="8"/>
Около 15% значений умножаются на 8 — обычные показания держатся у 50, выбросы
попадают ближе к 400. Выброс — это ровно базовое значение, умноженное на
коэффициент. Работает только с числами; нечисловые значения проходят мимо
нетронутыми (см. Детали).
До и после — тот же ряд с выбросами и без
Проблема. Если показать только «грязную» колонку, всплеск не отличишь от
по-настоящему большого значения: 460 — это сбой или просто крупное показание?
Нужен чистый эталон прямо рядом.
Инструмент. Берём один список показаний с order="sequential"
(строго по порядку) и строим из него две колонки: Clean — как есть, и Dirty —
тот же ряд, но с anomaly. Построчно базовое значение совпадает, поэтому каждый
всплеск очевиден: это то же число, умноженное на коэффициент.
<env count="12" seed="demo">
<sequence name="Clean"><gen type="text" value="48,50,46,52,49,51,47,53,50,48,52,49" order="sequential"/></sequence>
<sequence name="Dirty"><gen type="text" value="48,50,46,52,49,51,47,53,50,48,52,49" order="sequential" anomaly="0.3" anomaly_factor="10"/></sequence>
</env>
...
<data>норма=${{Clean}} | с выбросами=${{Dirty}}</data>
норма=48 | с выбросами=48 норма=50 | с выбросами=50 норма=46 | с выбросами=460 норма=52 | с выбросами=52 норма=49 | с выбросами=49 норма=51 | с выбросами=510 норма=47 | с выбросами=47 норма=53 | с выбросами=53 норма=50 | с выбросами=50 норма=48 | с выбросами=480 норма=52 | с выбросами=52 норма=49 | с выбросами=49
Три строки выбились: 46 → 460, 51 → 510, 48 → 480 — ровно ×10. Никаких пометок
«← выброс» не нужно: эталон слева, всплеск справа, разница говорит сама за себя. На
12 строках при anomaly="0.3" всплесков вышло 3 (доля случайная, а на маленькой
выборке разброс большой).
Зачем/когда. Такое сравнение бок о бок — самый быстрый способ убедиться, что
anomaly делает именно то, что вы думаете, прежде чем разворачивать это на реальном
датасете.
Насколько большой всплеск — anomaly_factor
Проблема. «Мягкий» выброс (чуть выше нормы) и «жёсткий» (в разы больше) ловятся разными порогами. Нужно уметь регулировать высоту всплеска.
Инструмент. Меняем только anomaly_factor; всё остальное оставляем как есть — ту
же последовательность, тот же seed, ту же anomaly="0.25". При фиксированных сиде,
имени и доле всплеск случается на тех же строках каждый раз — меняется лишь их
высота.
<gen type="text" value="48,50,46,52,49,51,47,53,50,48,52,49"
order="sequential" anomaly="0.25" anomaly_factor="5"/> <!-- затем 10, затем 20 -->
anomaly_factor="5" → "10" → "20", те же 12 строк в каждом:
factor=5 factor=10 factor=20 240 480 960 50 50 50 230 460 920 52 52 52 49 49 49 51 51 51 235 470 940 265 530 1060 50 50 50 48 48 48 52 52 52 245 490 980
База на строке 8 — 53: 53×5=265, 53×10=530, 53×20=1060. Строки-выбросы
(1, 3, 7, 8, 12) одни и те же во всех трёх колонках — anomaly_factor управляет
только высотой, а не тем, какие строки всплеснут. (Какие именно строки
всплеснут — зависит от seed и имени последовательности: переименуйте
последовательность — и всплески сдвинутся.)
Зачем/когда. Прогоните коэффициент по диапазону, чтобы найти, где порог вашего детектора начинает и перестаёт срабатывать, ничего больше не трогая.
Сколько выбросов — доля anomaly
Проблема. Редкий сбой (раз в сотню) и постоянный шум требуют разных порогов.
Само значение anomaly задаёт, как часто происходит всплеск.
Инструмент. Фиксируем базовое значение на 50, чтобы всплеск (500) был виден
с одного взгляда. Левая колонка anomaly="0.1", правая — anomaly="0.5", 20 строк:
<env count="20" seed="demo">
<sequence name="Low"> <gen type="text" value="50" order="sequential" anomaly="0.1" anomaly_factor="10"/></sequence>
<sequence name="High"><gen type="text" value="50" order="sequential" anomaly="0.5" anomaly_factor="10"/></sequence>
</env>
...
<data>доля 0.1: ${{Low}} доля 0.5: ${{High}}</data>
доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 500 доля 0.1: 500 доля 0.5: 50 доля 0.1: 50 доля 0.5: 500 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 50 доля 0.1: 50 доля 0.5: 500
Левая колонка всплеснула 1 раз из 20, правая — 11: чем выше доля, тем гуще выбросы.
anomaly сочетается с чем угодно — с диапазоном, с
распределением или с missing.
Зачем/когда. Подбирайте долю под моделируемое явление — редкий дефект на 0.01,
болтливый неисправный датчик на 0.4.
Колонка-ответ (ground-truth) — anomaly_flag
Проблема. Выбросы впрыснуты — но чтобы оценить детектор, нужен правильный ответ: какие именно строки аномальны. Глядя только на числа, сбойный всплеск не отличишь от законно большого показания.
Инструмент. Добавьте anomaly_flag="Имя" рядом с anomaly, и TDC заводит
новую колонку — последовательность Имя со значениями true/false, где true стоит
ровно на тех строках, что всплеснули:
<gen type="number" distribution="normal" mean="50" sd="3"
anomaly="0.2" anomaly_factor="8" anomaly_flag="IsOutlier"/>
...
<data>${{Reading}},${{IsOutlier}}</data>
360,true 48,false 49,false 51,false 52,false 52,false 47,false 49,false
360 (≈ 45 × 8) помечен true; обычные показания — false. Метка считается тем
же решением, что и сам выброс, поэтому эти двое не могут разойтись — и так во всех
движках, при любом объёме.
Метка — обычная колонка, поэтому её можно фильтровать через
if. Оставим только выбросы через
if="IsOutlier" (читается «истинность» строки, как у встроенных
_first/_last):
<block><line if="IsOutlier"><data>выброс: ${{Reading}}</data></line></block>
выброс: 360 выброс: 392 выброс: 400
Из 20 показаний три оказались аномалиями — готовый размеченный датасет для оценки точности и полноты детектора.
Зачем/когда. Всякий раз, когда детектор нужно измерить, а не просто накормить.
Учтите: anomaly_flag без anomaly — ошибка, помечать нечего.
Свой выброс — flag на <mix>
Проблема. anomaly="p" умеет только умножать. Иногда выброс должен выглядеть
иначе — со своим диапазоном, своим текстом, редким, но допустимым событием, — и вам
всё ещё нужна честная колонка-ответ, говорящая, какие строки подложные.
Инструмент. Опишите выброс отдельной веткой <mix>,
пометьте этот <case> через anomaly="true" и попросите у
mix колонку-ответ через flag="Имя". Здесь
температура обычно 20–24, но в четверти случаев залипший датчик показывает
90–99:
<env count="12" seed="sensor-2026">
<sequence name="Id"><gen type="increment" value="1"/></sequence>
<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>
</env>
<block>
<line><data>${{Id}},${{Temp}},${{Bad}}</data></line>
</block>
Колонки — id, temp, bad:
1,23,false 2,21,false 3,22,false 4,21,false 5,24,false 6,23,false 7,21,false 8,92,true 9,20,false 10,97,true 11,21,false 12,98,true
Три строки из двенадцати — ровно 25%, как и просили в
percent, — и bad равно true на каждой из них. Метка
берётся из того же выбора, которым выбиралась ветка, поэтому разойтись со
значением она не может.
Зачем/когда. anomaly="true" на <case> — это только ярлык: он ничего не
подмешивает и ничего не меняет; выброс целиком делает собственный генератор ветки
(<gen type="number" value="90..99"/>). А значит, вы полностью управляете тем, как
выглядит выброс, — в отличие от anomaly="p", который умеет только масштабировать
число.
Обе половины работают только вместе
TDC проверяет, что и ярлык, и колонка на месте:
| Что написали | Что скажет TDC |
|---|---|
anomaly="true", но у <mix> нет flag= | ошибка TDC203 — ярлыку некуда попасть |
flag=, но ни один <case> не помечен | предупреждение TDC202 — колонка будет вся false |
flag= на вложенном <mix> | ошибка TDC203 — колонку заводит только именованный mix |
В Parquet — настоящий тип
При выгрузке в Parquet колонка-ответ становится честным BOOLEAN, а значения
сохраняют свой числовой тип, и нигде никакого type=:
id INT64 REQUIRED temp INT64 REQUIRED bad BOOLEAN REQUIRED
Тип значений выводится, только если все ветки согласны между собой (здесь обе — числа). Если одна ветка возвращает число, а другая слово, колонка останется текстом — TDC не угадывает тип, в котором не уверен. См. Форматы вывода.
Ещё варианты применения
- Проверка чистильщика данных. Пометьте ветку, выдающую мусор, — кривой адрес, пустой телефон — и посмотрите, сколько помеченных строк чистильщик на самом деле убрал.
- Несколько видов брака. Пометить можно не одну ветку;
flagвстаёт вtrueна всех помеченных случаях сразу. - Разметка «редкого события». Ярлык не обязан означать поломку — он может помечать редкий, но вполне законный случай, который модель всё равно должна уметь ловить.
Детали
- Детерминированно. Тот же
seedдаёт те же выбросы, на тех же строках. См. Детерминизм и пропорции. - Любой движок, любой объём. Выбросы решаются по номеру строки, поэтому прогоны в памяти и на диске совпадают точь-в-точь.
- Только числа, и решает значение, а не
type=. В примерах выше список записан черезtype="text", но значения — числа, поэтомуanomalyих умножает. Поставьте его на колонку имён — значения пройдут без изменений, причём молча:tdcv2 checkне скажет ничего, потому что текстовый список вполне может содержать числа, и TDC не знает, что вы имели в виду. Если колонка, в которой должны быть всплески, вышла чистой, проверять надо в первую очередь это. anomaly="0"означает отсутствие выбросов.
Смотрите также
- Числовой генератор — диапазоны и распределения, к
которым цепляется
anomaly. - Обзор генераторов —
missing=иanomaly=бок о бок, оба применяются уже после того, как значение произведено. - Справочник тегов —
<mix>и<case>, ветвление, на котором строится способ со своим выбросом. - Вывод и форматирование — выражение
if, которым фильтруют по колонке-ответу.