Проверка данных на этапе трансформации

Модель данных и влияние типов на валидацию

Vega и Vega-Lite используют строгую типизацию полей, которая задаётся в канале encoding. Именно здесь формируется первичный уровень «неявной проверки»:

  • quantitative — числовые значения, приводятся к числу
  • temporal — даты и время, парсятся в Date
  • ordinal — упорядоченные категории
  • nominal — категориальные значения без порядка

При несоответствии типов происходит автоматическое приведение, которое часто выступает первой линией обнаружения ошибок. Например, строка "42" может быть интерпретирована как число, а некорректная дата — превращена в Invalid Date, что далее может быть отфильтровано.

{
  "mark": "line",
  "encoding": {
    "x": {"field": "date", "type": "temporal"},
    "y": {"field": "value", "type": "quantitative"}
  }
}

Если поле date содержит некорректные строки, они будут либо проигнорированы, либо приведены к невалидному значению в зависимости от дальнейших трансформаций.


Фильтрация некорректных значений через transform

Основной механизм явной проверки данных в Vega-Lite — трансформация filter. Она позволяет исключать строки на основе выражений, написанных на языке выражений Vega.

Типичные задачи:

  • удаление null и undefined
  • исключение NaN
  • отбрасывание отрицательных значений
  • контроль диапазонов
  • проверка строковых шаблонов
{
  "transform": [
    {
      "filter": "datum.value != null && isValid(datum.value)"
    }
  ]
}

Функция isValid в Vega выражениях позволяет отделять корректные значения от NaN, null и undefined.

Более строгая проверка числового диапазона:

{
  "transform": [
    {
      "filter": "datum.value >= 0 && datum.value <= 100"
    }
  ]
}

Обработка пропущенных значений

Пропуски в данных являются одним из наиболее частых источников визуальных искажений. Vega-Lite не выполняет автоматическую иммутацию (в базовой конфигурации), поэтому стратегия обработки задаётся явно:

Исключение пропусков

{
  "transform": [
    {
      "filter": "datum.value != null"
    }
  ]
}

Замена через calculate

Использование calculate позволяет формировать производные поля с подстановкой значений по умолчанию:

{
  "transform": [
    {
      "calculate": "datum.value != null ? datum.value : 0",
      "as": "value_filled"
    }
  ]
}

Такой подход создаёт новый слой данных без изменения исходного набора.


Валидация структуры через calculate и логические выражения

В Vega выражения поддерживают полноценную логическую алгебру, что позволяет строить многоуровневые проверки.

Пример проверки корректности записи:

{
  "transform": [
    {
      "calculate": "isValid(datum.value) && datum.value > 0 ? datum.value : null",
      "as": "validated_value"
    }
  ]
}

Дальнейшая фильтрация:

{
  "transform": [
    {
      "filter": "datum.validated_value != null"
    }
  ]
}

Так формируется двухэтапная система проверки:

  1. вычисление валидности
  2. отбрасывание некорректных значений

Проверка временных данных

Временные ряды требуют отдельного уровня контроля, поскольку ошибки парсинга дат могут полностью исказить ось времени.

При указании типа temporal Vega-Lite пытается автоматически распознать формат:

{
  "encoding": {
    "x": {"field": "timestamp", "type": "temporal"}
  }
}

Однако при нестандартных форматах требуется явное преобразование:

{
  "transform": [
    {
      "calculate": "toDate(datum.timestamp)",
      "as": "parsed_time"
    }
  ]
}

Дальнейшая проверка:

{
  "transform": [
    {
      "filter": "isValid(datum.parsed_time)"
    }
  ]
}

Особое значение имеет проверка диапазонов дат:

{
  "transform": [
    {
      "filter": "datum.parsed_time >= datetime(2020, 0, 1)"
    }
  ]
}

Контроль диапазонов и бизнес-правил

Проверка данных часто выходит за рамки синтаксической корректности и включает бизнес-ограничения.

Пример:

  • значение не может превышать 1000
  • процент должен быть в диапазоне 0–100
  • температура не может быть ниже абсолютного минимума
{
  "transform": [
    {
      "filter": "datum.temperature >= -273.15"
    }
  ]
}

Многоуровневая проверка:

{
  "transform": [
    {
      "filter": "datum.value != null && datum.value >= 0 && datum.value <= 100"
    }
  ]
}

Предобработка строковых данных

Строковые поля часто содержат шум: лишние пробелы, разные регистры, нестандартизированные категории.

Используются выражения нормализации:

{
  "transform": [
    {
      "calculate": "lower(trim(datum.category))",
      "as": "category_norm"
    }
  ]
}

После нормализации выполняется контроль допустимых значений:

{
  "transform": [
    {
      "filter": "indexof(['a','b','c'], datum.category_norm) >= 0"
    }
  ]
}

Выявление выбросов на этапе трансформации

Vega-Lite не предоставляет встроенных статистических детекторов выбросов, но позволяет реализовать их вручную через агрегации и window-трансформации.

Сначала вычисляются статистики:

{
  "transform": [
    {
      "aggregate": [
        {"op": "mean", "field": "value", "as": "mean_value"},
        {"op": "stdev", "field": "value", "as": "std_value"}
      ]
    }
  ]
}

Далее применяется фильтрация:

{
  "transform": [
    {
      "filter": "datum.value < datum.mean_value + 3 * datum.std_value"
    }
  ]
}

В более сложных сценариях используется joinaggregate, позволяющий сохранить исходные строки и статистику одновременно.


Согласование типов и устранение конфликтов схемы

Одной из частых проблем является смешение типов в одном поле: числовые и строковые значения в одной колонке. Vega-Lite решает это через явное приведение:

{
  "transform": [
    {
      "calculate": "toNumber(datum.value)",
      "as": "value_num"
    }
  ]
}

После приведения выполняется проверка:

{
  "transform": [
    {
      "filter": "isValid(datum.value_num)"
    }
  ]
}

Многоэтапные пайплайны проверки

На практике валидация данных строится как последовательность трансформаций:

  1. Приведение типов
  2. Нормализация строк
  3. Фильтрация пропусков
  4. Проверка диапазонов
  5. Удаление выбросов
  6. Подготовка производных полей

Пример комбинированного pipeline:

{
  "transform": [
    {
      "calculate": "toNumber(datum.value)",
      "as": "value_num"
    },
    {
      "filter": "isValid(datum.value_num)"
    },
    {
      "filter": "datum.value_num >= 0"
    },
    {
      "calculate": "lower(trim(datum.category))",
      "as": "category_norm"
    },
    {
      "filter": "datum.category_norm != ''"
    }
  ]
}

Использование lookup как механизма косвенной валидации

Операция lookup позволяет проверять согласованность данных через внешние справочники.

Пример проверки категорий:

{
  "transform": [
    {
      "lookup": "category",
      "from": {
        "data": {
          "values": [
            {"key": "a"},
            {"key": "b"}
          ]
        },
        "key": "key",
        "fields": ["key"]
      }
    }
  ]
}

Если соответствие не найдено, поле становится null, что далее может быть отфильтровано.

{
  "transform": [
    {
      "filter": "datum.key != null"
    }
  ]
}

Поведение визуализации при невалидных данных

После трансформации некорректные значения:

  • исключаются через filter
  • заменяются через calculate
  • приводятся к null и игнорируются mark-слоем
  • вызывают пропуски в линиях (для line/area графиков)

Линейные графики автоматически разрываются при наличии null, что косвенно служит индикатором проблем в данных.


Контроль качества данных через комбинацию signal и transform (Vega)

В Vega более низкоуровневый подход позволяет вводить сигналы для динамической проверки данных. Это даёт возможность строить интерактивную валидацию:

  • фильтрация по пользовательским параметрам
  • переключение режимов проверки
  • динамическое исключение диапазонов

Сигналы комбинируются с фильтрами:

{
  "signals": [
    {"name": "minValue", "value": 0}
  ],
  "data": [
    {
      "transform": [
        {
          "filter": "datum.value >= minValue"
        }
      ]
    }
  ]
}

Ошибки данных как часть вычислительного конвейера

Vega/Vega-Lite рассматривают некорректные данные не как исключения времени выполнения, а как часть потока значений. Это приводит к следующим особенностям:

  • ошибки не прерывают визуализацию
  • некорректные значения становятся null
  • фильтрация выполняется декларативно
  • логика проверки полностью описывается в JSON-структуре

Такой подход делает проверку данных не отдельным этапом, а встроенной частью трансформационного слоя визуализации.