Пропуски в данных — missing
Когда пригодится — когда нужно проверить, как код переживает дыры.
Настоящие данные почти никогда не бывают полными: где-то не заполнили телефон,
где-то потерялся доход, где-то поле просто пустое. Если тестировать только на
«идеальных» строках, первый же null в проде всё сломает. Атрибут missing
подмешивает пробелы специально, чтобы вы увидели поломку раньше своих
пользователей.
missing — сквозной атрибут: он работает на любом
<gen>, что бы тот ни генерировал.
Примеры вывода ниже — иллюстративные: конкретные строки, которые даст тот или
иной seed, могут отличаться между версиями ядра, но поведение, которое
гарантирует атрибут, — нет.
- Aчистый ряд
- Bс anomaly= — отмеченные точки это подмешанные выбросы
- Cс missing= — засечки внизу это строки, где значения нет
Как включить
Поставьте missing="p" на <gen>, где p — доля от 0 до 1, то есть доля
значений, которые станут пустыми:
<gen type="number" value="30000..90000" missing="0.3"/>
Примерно 30% доходов выйдут пустыми, остальные — обычными числами.
Каждое значение «теряется» независимо с этой вероятностью. В статистике это называется MCAR — Missing Completely At Random, «пропуски совершенно случайны»: дыры выпадают безотносительно к другим полям и к самому значению.
По умолчанию пропуск — это пустая строка. Хотите свой маркер (NULL, NA,
—)? Задайте missing_as — см. раздел ниже.
До и после — что именно меняет пропуск
Проблема. Пока не увидите одно и то же поле «целым» и «дырявым» рядом, трудно понять, что вообще происходит: где значение потерялось, а где его и не было.
Инструмент. Возьмём один список городов и сделаем из него две колонки:
Full — как есть, и Holey — тот же список, но с missing.
order="sequential" берёт значения строго по порядку,
поэтому обе колонки построчно совпадают — видно ровно то, что «выпало»:
<env count="8" seed="demo">
<sequence name="Full"> <gen type="text" value="Москва,Питер,Казань,Пермь,Сочи,Омск,Тула,Уфа" order="sequential"/></sequence>
<sequence name="Holey"><gen type="text" value="Москва,Питер,Казань,Пермь,Сочи,Омск,Тула,Уфа" order="sequential" missing="0.4"/></sequence>
</env>
...
<data>полное=${{Full}} | с пропусками=${{Holey}}</data>
полное=Москва | с пропусками=Москва полное=Питер | с пропусками=Питер полное=Казань | с пропусками= полное=Пермь | с пропусками= полное=Сочи | с пропусками=Сочи полное=Омск | с пропусками=Омск полное=Тула | с пропусками=Тула полное=Уфа | с пропусками=Уфа
Слева всё на месте; справа — те же строки, но Казань и Пермь исчезли.
Значение не заменилось на другое и не сдвинулось вверх — оно просто стало
пустым. (На 8 строках при missing="0.4" выпало 2 — доля случайная, и на
маленькой выборке разброс большой.)
Свой маркер вместо пустоты — missing_as
Проблема. Пустая строка в выгрузке невидима: в CSV это «ничего между двумя
запятыми», глазами не отличить пропуск от короткого значения. В настоящих данных
дыру обычно помечают явно — NULL, NA, —.
Инструмент. missing_as="маркер" печатает ваш текст вместо пустоты. Строки,
где случился пропуск, — те же самые (их выбирает seed и имя колонки, а не
маркер); меняется только то, что стоит на месте дыры:
<sequence name="Holey">
<gen type="text" value="Москва,Питер,Казань,Пермь,Сочи,Омск,Тула,Уфа"
order="sequential" missing="0.4" missing_as="NULL"/>
</sequence>
полное=Москва | с пропусками=Москва полное=Питер | с пропусками=Питер полное=Казань | с пропусками=NULL полное=Пермь | с пропусками=NULL полное=Сочи | с пропусками=Сочи полное=Омск | с пропусками=Омск полное=Тула | с пропусками=Тула полное=Уфа | с пропусками=Уфа
Дыры на тех же строках 3 и 4, что и раньше, — но теперь их видно. Поставьте
missing_as="—" или missing_as="NA" и получите свой маркер на тех же местах.
Сколько дырок — вариации доли
Проблема. Хочется понять, как поведёт себя система при «редких» и при «частых» пропусках. Одна доля этого не покажет.
Инструмент. Меняем только missing, всё остальное то же. X — значение на
месте, [] — дыра. Три колонки, 20 строк:
<env count="20" seed="demo">
<sequence name="A"><gen type="text" value="X" order="sequential" missing="0.1"/></sequence>
<sequence name="B"><gen type="text" value="X" order="sequential" missing="0.3"/></sequence>
<sequence name="C"><gen type="text" value="X" order="sequential" missing="0.6"/></sequence>
</env>
...
<data>0.1:[${{A}}] 0.3:[${{B}}] 0.6:[${{C}}]</data>
0.1:[X] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[] 0.6:[X] 0.1:[X] 0.3:[X] 0.6:[X] 0.1:[X] 0.3:[] 0.6:[X] 0.1:[X] 0.3:[] 0.6:[] 0.1:[X] 0.3:[] 0.6:[X] 0.1:[X] 0.3:[X] 0.6:[] 0.1:[] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[X] 0.6:[X] 0.1:[] 0.3:[X] 0.6:[X] 0.1:[X] 0.3:[X] 0.6:[X] 0.1:[X] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[] 0.6:[] 0.1:[X] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[X] 0.6:[X] 0.1:[X] 0.3:[X] 0.6:[X] 0.1:[X] 0.3:[X] 0.6:[] 0.1:[X] 0.3:[X] 0.6:[X]
Считаем пустые [] по колонкам: 0.1 → 2 дыры из 20, 0.3 → 5, 0.6 → 10.
Доля растёт — дыр становится больше. Точных 2 / 6 / 12 не будет: каждое значение
теряется независимо, поэтому на 20 строках счёт «гуляет» вокруг ожидаемого.
Пропуски в нескольких полях
missing сочетается с чем угодно — с обычным диапазоном, с
template, с шаблоном
regex, со статистическим распределением. Соберём
запись из трёх полей: имя (без пропусков), телефон (missing="0.3", маркер
—) и доход (missing="0.25", маркер по умолчанию — пусто):
<env count="8" seed="rec" local="ru">
<sequence name="Name"> <gen type="template" value="person.male.firstName"/></sequence>
<sequence name="Phone"> <gen type="regex" value="\+7 \(9[0-9]{2}\) [0-9]{3}-[0-9]{2}-[0-9]{2}" missing="0.3" missing_as="—"/></sequence>
<sequence name="Income"> <gen type="number" value="30000..90000" missing="0.25"/></sequence>
</env>
...
<data>${{Name}} | тел: ${{Phone}} | доход: ${{Income}}</data>
Дмитрий | тел: +7 (922) 889-53-97 | доход: 38370 Тимофей | тел: +7 (906) 344-33-99 | доход: 70315 Станислав | тел: — | доход: 41008 Вадим | тел: +7 (918) 997-61-79 | доход: 85063 Григорий | тел: +7 (952) 879-35-78 | доход: Евгений | тел: +7 (914) 240-78-00 | доход: 48334 Демьян | тел: — | доход: 46153 Ярослав | тел: +7 (929) 079-00-13 | доход: 33986
У части строк телефон — —, у пятой строки пустой доход (маркер по умолчанию —
ничего). Пропуски в двух полях независимы: дыра в одной колонке ничего не
говорит о другой.
Детали
- Детерминированно. Тот же
seedдаёт те же пропуски — см. Детерминизм и пропорции. Один и тот же «дырявый» датасет воспроизводится байт в байт, запуск за запуском. - Оба движка, любой объём. Пропуски считаются по номеру строки, поэтому память не растёт с размером датасета — см. Большие выгрузки и стриминг.
missing="0"— совсем без пропусков, ровно как если бы атрибута не было.
Сейчас реализован только MCAR: пропуски равновероятны и ни от чего не зависят. В планах — MAR / MNAR, где вероятность дыры зависит от другого поля или от самого значения.
Смотрите также
- Последовательности — те самые колонки,
в которых вы пробиваете дыры, и как
${{Name}}их читает. - Текст и Числа — генераторы из примеров выше.
- Маски и регистр —
order="sequential"и остальные сквозные атрибуты генераторов.