Фасетинг: facet

Фасетинг в Vega-Lite представляет собой механизм разбиения одного набора данных на множество подграфиков (панелей), каждая из которых отображает отдельную подвыборку данных в едином визуальном шаблоне. Концепция основана на идее small multiples: повторяющихся графиков одинаковой структуры, но с разными срезами данных. Это позволяет одновременно сравнивать распределения, тренды и зависимости между группами, сохраняя единообразие визуального кодирования.

Фасетинг в Vega и Vega-Lite реализуется декларативно: описывается не процесс построения графиков, а структура разбиения данных и правила отображения. Основная логика сосредоточена вокруг свойства facet, которое определяет поле группировки и способ компоновки панелей.


Фасетинг опирается на группировку данных по категориальному признаку. Каждое уникальное значение выбранного поля становится отдельной панелью.

Минимальная структура:

{
  "data": { "url": "data.csv" },
  "facet": {
    "row": { "field": "category", "type": "nominal" }
  },
  "spec": {
    "mark": "bar",
    "encoding": {
      "x": { "field": "value", "type": "quantitative" },
      "y": { "field": "name", "type": "nominal" }
    }
  }
}

Здесь:

  • facet.row задаёт разбиение по строкам
  • каждое значение category создаёт отдельную панель
  • spec описывает внутренний график, повторяемый для каждой группы

Фасетинг не изменяет исходный датасет, он лишь логически сегментирует его перед отрисовкой.


Параметры facet: row и column

Фасетинг может быть одномерным или двумерным.

Разбиение по строкам

"facet": {
  "row": {
    "field": "region",
    "type": "nominal"
  }
}

Каждая строка сетки соответствует отдельной категории.

Разбиение по столбцам

"facet": {
  "column": {
    "field": "year",
    "type": "ordinal"
  }
}

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

Двумерная сетка

"facet": {
  "row": { "field": "region", "type": "nominal" },
  "column": { "field": "product", "type": "nominal" }
}

Такой подход формирует матрицу подграфиков, где каждая ячейка — комбинация двух категорий. Это классический случай применения фасетинга в аналитических панелях.


facet через fieldDef и масштабирование данных

Поле фасетинга может быть дополнено преобразованием:

  • агрегацией
  • биннингом
  • временной дискретизацией

Пример с бинами:

"facet": {
  "column": {
    "field": "age",
    "bin": true,
    "type": "quantitative"
  }
}

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


Связь фасетинга и spec

Ключевая особенность архитектуры Vega-Lite — вложенный spec. Он определяет, что именно рисуется внутри каждой панели.

{
  "facet": {
    "row": { "field": "country", "type": "nominal" }
  },
  "spec": {
    "mark": "line",
    "encoding": {
      "x": { "field": "date", "type": "temporal" },
      "y": { "field": "value", "type": "quantitative" }
    }
  }
}

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


Масштабы и их согласование (resolve)

Один из центральных аспектов фасетинга — управление шкалами.

По умолчанию Vega-Lite может:

  • либо синхронизировать оси между всеми панелями
  • либо давать каждой панели независимые шкалы

Это регулируется через resolve:

"resolve": {
  "scale": {
    "y": "independent"
  }
}

Типы поведения:

  • "shared" — общий масштаб для всех панелей
  • "independent" — отдельный масштаб для каждой панели

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


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

Vega-Lite автоматически рассчитывает сетку, но поведение можно контролировать через параметры компоновки:

  • spacing — расстояние между панелями
  • columns — количество столбцов (для row-facet)
  • align — выравнивание осей

Пример:

{
  "facet": {
    "row": { "field": "category", "type": "nominal" },
    "columns": 3
  },
  "spec": {
    "mark": "bar",
    "encoding": {
      "x": { "field": "value", "type": "quantitative" },
      "y": { "field": "item", "type": "nominal" }
    }
  }
}

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


Агрегация внутри фасетов

Фасетинг часто комбинируется с агрегированными полями. Внутри каждой панели можно вычислять статистику отдельно:

"encoding": {
  "x": {
    "aggregate": "mean",
    "field": "value",
    "type": "quantitative"
  }
}

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

Важно, что агрегация выполняется после разделения данных по фасетам, а не до него.


Фасетинг и повторение (facet vs repeat)

В Vega-Lite существует два механизма многократного отображения:

  • facet — разделение данных по группам
  • repeat — повторение одного графика с разными полями

Различие фундаментальное:

Механизм Основание Результат
facet данные группы панелей
repeat поля набор одинаковых графиков

Пример repeat:

"repeat": {
  "column": ["a", "b", "c"]
}

Пример facet:

"facet": {
  "column": { "field": "type", "type": "nominal" }
}

Вложенные фасеты

Фасетинг может быть вложенным, создавая иерархические структуры:

{
  "facet": {
    "row": { "field": "continent", "type": "nominal" }
  },
  "spec": {
    "facet": {
      "column": { "field": "country", "type": "nominal" }
    },
    "spec": {
      "mark": "line",
      "encoding": {
        "x": { "field": "year", "type": "temporal" },
        "y": { "field": "gdp", "type": "quantitative" }
      }
    }
  }
}

Такая структура формирует многоуровневую сетку: континенты → страны → временные ряды.


Работа с пустыми значениями и фильтрацией фасетов

Фасетинг чувствителен к пропущенным данным. Vega-Lite предоставляет несколько стратегий:

  • исключение null-значений
  • явная группировка “Unknown”
  • предварительная трансформация данных

Пример фильтрации:

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

Это позволяет избежать появления пустых панелей.


Сортировка фасетов

Порядок панелей не всегда совпадает с лексикографическим порядком значений. Его можно контролировать:

"facet": {
  "row": {
    "field": "category",
    "type": "nominal",
    "sort": ["A", "B", "C"]
  }
}

Также возможна сортировка по агрегированным значениям:

"sort": { "op": "mean", "field": "value", "order": "descending" }

Производительность фасетинга

При большом количестве уникальных значений фасетинг становится ресурсоёмким:

  • каждая категория создаёт отдельный график
  • увеличивается DOM-структура
  • возрастает нагрузка на рендеринг осей и шкал

Практическое ограничение часто связано не с Vega-Lite, а с браузерным отрисовщиком.

Оптимизации:

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

Стилизация и унификация панелей

Все панели наследуют единый визуальный стиль, но допускают локальные переопределения:

"spec": {
  "mark": {
    "type": "line",
    "strokeWidth": 2
  }
}

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

"color": {
  "condition": {
    "test": "datum.value > 100",
    "value": "red"
  },
  "value": "blue"
}

Фасетинг как инструмент аналитической декомпозиции

Фасетинг в Vega-Lite выступает не просто механизмом компоновки, а способом структурирования анализа:

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

Каждая панель становится независимой аналитической единицей, но остаётся частью общей визуальной системы, управляемой единым спецификационным описанием.