Поле aggregate в кодировании

В Vega-Lite агрегирование в кодировании задаёт способ преобразования множества исходных значений в одно итоговое значение непосредственно на уровне канала. Поле aggregate в описании encoding определяет, как именно будут свёрнуты данные внутри группировки, формируемой остальными каналами (чаще всего x, y, color, detail, row, column).

Семантически агрегирование в encoding выполняет ту же роль, что и SQL-агрегаты в GROUP BY, но выражается декларативно внутри спецификации визуализации.

Основная структура агрегирования в encoding

Типичная форма записи:

{
  data: { values: [...] },
  mark: "bar",
  encoding: {
    x: { field: "category", type: "nominal" },
    y: { aggregate: "sum", field: "value", type: "quantitative" }
  }
}

В этом примере происходит группировка по category, а затем вычисляется сумма value внутри каждой группы. Канал y получает уже агрегированное значение.

Ключевое свойство:

  • aggregate применяется к каналу, а не к данным в целом
  • агрегирование выполняется после неявной группировки по остальным каналам

Модель группировки и роль encode-каналов

Любой канал в encoding можно рассматривать как фактор группировки, если он не агрегирован. Например:

  • x: category создаёт группы по категориям
  • color: region дополнительно дробит группы
  • detail добавляет скрытое измерение группировки без визуального канала

Когда задано:

encoding: {
  x: { field: "category", type: "nominal" },
  color: { field: "region", type: "nominal" },
  y: { aggregate: "sum", field: "sales", type: "quantitative" }
}

фактически формируется группировка по паре (category, region), после чего вычисляется сумма sales.


Поддерживаемые агрегатные функции

Vega-Lite поддерживает набор стандартных агрегатов:

  • sum — сумма значений
  • mean — среднее арифметическое
  • median — медиана
  • min — минимум
  • max — максимум
  • count — количество записей
  • distinct — количество уникальных значений
  • variance — дисперсия
  • stdev — стандартное отклонение
  • argmin, argmax — значения, соответствующие минимуму/максимуму другого поля

Пример использования среднего значения:

y: { aggregate: "mean", field: "temperature", type: "quantitative" }

Особый случай: count без поля

Агрегат count отличается тем, что может не требовать field.

y: { aggregate: "count", type: "quantitative" }

В этом случае считается количество записей в каждой группе, независимо от содержимого полей.

Если поле указано явно:

y: { aggregate: "count", field: "id" }

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


Различие между aggregate в encoding и transform.aggregate

В Vega-Lite существует два подхода к агрегированию:

  1. агрегирование внутри encoding (encoding.y.aggregate)
  2. трансформация данных (transform: [{ aggregate: ... }])

Пример encoding-агрегации:

encoding: {
  x: { field: "type", type: "nominal" },
  y: { aggregate: "sum", field: "amount" }
}

Эквивалент через transform:

transform: [
  {
    aggregate: [{ op: "sum", field: "amount", as: "total" }],
    groupby: ["type"]
  }
],
encoding: {
  x: { field: "type" },
  y: { field: "total" }
}

Ключевое различие:

  • encoding-агрегация компактнее
  • transform даёт больше контроля и промежуточных вычислений

Приоритет агрегации и правила конфликтов

Если в encoding указан aggregate, Vega-Lite автоматически:

  • отключает прямое использование исходного значения поля
  • заменяет его агрегированным результатом
  • добавляет скрытую операцию groupby по остальным каналам

Если одновременно используются:

  • aggregate
  • bin
  • timeUnit

порядок обработки становится критичным.


Агрегация и биннинг (bin)

Комбинация bin и aggregate часто используется для гистограмм:

encoding: {
  x: { bin: true, field: "value", type: "quantitative" },
  y: { aggregate: "count" }
}

Здесь:

  1. значения value разбиваются на интервалы
  2. каждая корзина получает количество элементов

Фактически формируется распределение.


Агрегация и временные данные

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

x: { timeUnit: "year", field: "date" }
y: { aggregate: "mean", field: "price" }

Это приводит к:

  • группировке по году
  • вычислению среднего значения price в каждом году

Null-значения и поведение агрегатов

Null-значения в Vega-Lite обычно:

  • игнорируются в sum, mean, min, max
  • уменьшают влияние на count (если поле задано)
  • полностью исключаются из вычислений, если не участвуют явно

Это поведение важно при работе с неполными данными, так как агрегаты не требуют предварительной очистки датасета.


Агрегирование и множественные каналы

Если несколько каналов содержат агрегаты, возникает сложная группировка:

encoding: {
  x: { field: "category" },
  color: { field: "region" },
  y: { aggregate: "sum", field: "sales" },
  size: { aggregate: "mean", field: "profit" }
}

Здесь:

  • sum(sales) считается по (category, region)
  • mean(profit) считается по тем же группам

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


Агрегирование и сортировка

Агрегированные поля часто используются для сортировки:

encoding: {
  x: {
    field: "category",
    type: "nominal",
    sort: { op: "sum", field: "sales", order: "descending" }
  },
  y: { aggregate: "sum", field: "sales" }
}

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


Использование выражения aggregate в сложных визуализациях

Агрегирование становится основой для:

  • столбчатых диаграмм
  • тепловых карт
  • сгруппированных сравнений
  • сводных таблиц

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

{
  mark: "rect",
  encoding: {
    x: { field: "day", type: "ordinal" },
    y: { field: "hour", type: "ordinal" },
    color: { aggregate: "mean", field: "load", type: "quantitative" }
  }
}

Каждая ячейка представляет агрегированное значение по комбинации дня и часа.


Аргументация выбора агрегата

Разные агрегаты дают разную интерпретацию данных:

  • sum отражает общий объём
  • mean сглаживает распределение
  • median устойчив к выбросам
  • min/max фиксируют экстремумы
  • count показывает плотность наблюдений

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


Взаимодействие с scale и типами данных

Агрегированные значения влияют на:

  • автоматический диапазон шкалы (scale domain)
  • тип шкалы (linear, log)
  • интерпретацию осей

Например, при aggregate: "sum" Vega-Lite пересчитывает domain по суммарным значениям, а не по исходным записям.


Внутреннее преобразование данных

На уровне компиляции Vega-Lite преобразует encoding-агрегацию в:

  1. группировку по неагрегированным полям
  2. вычисление агрегатных функций
  3. генерацию нового набора данных (dataflow)
  4. привязку результата к каналам визуализации

В Vega (низкоуровневом формате) это уже выражается через явные aggregate трансформации.


Ошибки и неоднозначности

Типичные проблемы при использовании aggregate:

  • отсутствие field при неподдерживаемом агрегате
  • попытка агрегировать уже агрегированные данные без понимания двойной агрегации
  • несоответствие типов (например, mean по номинальному полю)
  • конфликт bin и aggregate при неправильной структуре encoding

Поведение при отсутствии группирующих полей

Если в encoding нет ни одного неагрегированного канала, Vega-Lite рассматривает весь набор данных как одну группу:

encoding: {
  y: { aggregate: "sum", field: "value" }
}

В этом случае результат — одно значение, отображаемое одной геометрией.