Трансформация identifier в библиотеке Vega предназначена
для генерации уникальных идентификаторов строк набора данных. Каждой
записи присваивается специальное поле с уникальным числовым значением,
которое сохраняется в рамках текущего жизненного цикла данных.
В экосистеме Vega-Lite эта трансформация особенно важна при:
Трансформация добавляет новое поле в каждую запись:
{
"type": "identifier",
"as": "id"
}
После обработки каждая строка получает уникальное значение:
[
{ "category": "A", "value": 10, "id": 1 },
{ "category": "B", "value": 20, "id": 2 },
{ "category": "C", "value": 30, "id": 3 }
]
identifier не анализирует содержимое данных и не
вычисляет хеши. Трансформация просто создаёт уникальный числовой
идентификатор для каждой строки.
Особенности:
{
"type": "identifier",
"as": "id"
}
| Параметр | Тип | Описание |
|---|---|---|
type |
string | Тип трансформации |
as |
string | Имя создаваемого поля |
Исходные данные:
[
{ "name": "Alice", "score": 15 },
{ "name": "Bob", "score": 22 },
{ "name": "Carol", "score": 17 }
]
Трансформация:
{
"transform": [
{
"type": "identifier",
"as": "id"
}
]
}
Результат:
[
{ "name": "Alice", "score": 15, "id": 1 },
{ "name": "Bob", "score": 22, "id": 2 },
{ "name": "Carol", "score": 17, "id": 3 }
]
В Vega-Lite identifier обычно применяется через массив
transform.
Пример:
{
"data": {
"values": [
{ "x": 1, "y": 5 },
{ "x": 2, "y": 9 },
{ "x": 3, "y": 7 }
]
},
"transform": [
{
"identifier": "uid"
}
],
"mark": "point",
"encoding": {
"x": { "field": "x", "type": "quantitative" },
"y": { "field": "y", "type": "quantitative" }
}
}
После преобразования каждая точка получает поле uid.
Часто возникает путаница между identifier и
calculate.
{
"calculate": "datum.x + '-' + datum.y",
"as": "id"
}
{
"type": "identifier",
"as": "id"
}
Ключевое различие:
| identifier | calculate |
|---|---|
| Создаёт уникальный ID | Вычисляет выражение |
| Не зависит от данных | Зависит от значений |
| Гарантирует уникальность | Не гарантирует уникальность |
| Использует внутренний счётчик | Использует формулу |
Одна из важнейших областей применения — корректная работа анимаций и transitions.
При обновлении данных визуализация должна понимать:
Без стабильного идентификатора система не может сопоставить элементы между состояниями.
Исходный набор:
[
{ "x": 1, "y": 10 },
{ "x": 2, "y": 20 }
]
Новый набор:
[
{ "x": 1, "y": 15 },
{ "x": 2, "y": 25 }
]
Без идентификаторов движок может интерпретировать изменения как:
Это приводит к неправильной анимации.
identifier позволяет сохранить связь между
элементами.
{
"type": "identifier",
"as": "id"
}
Теперь объект:
{ "x": 1, "y": 10, "id": 1 }
после обновления остаётся тем же логическим объектом:
{ "x": 1, "y": 15, "id": 1 }
При потоковой визуализации данные постоянно добавляются.
Пример:
[
{ "time": 1, "value": 10 },
{ "time": 2, "value": 14 }
]
Через некоторое время:
[
{ "time": 3, "value": 18 }
]
Трансформация:
{
"type": "identifier",
"as": "eventId"
}
создаёт уникальный идентификатор для каждой новой записи.
Это особенно важно в:
Трансформация lookup часто требует уникальных
ключей.
Таблица пользователей:
[
{ "name": "Alice" },
{ "name": "Bob" }
]
После identifier:
[
{ "name": "Alice", "id": 1 },
{ "name": "Bob", "id": 2 }
]
Теперь можно выполнять join:
{
"type": "lookup",
"from": "orders",
"key": "userId",
"fields": ["id"],
"values": ["amount"]
}
Во многих интерактивных сценариях необходимы уникальные ключи.
Например:
{
"transform": [
{
"identifier": "id"
}
]
}
Теперь каждая запись имеет стабильный ключ, который можно использовать в интерактивных механизмах.
Важно понимать: identifier выполняется в определённом
порядке конвейера.
{
"transform": [
{
"type": "identifier",
"as": "id"
},
{
"type": "filter",
"expr": "datum.value > 10"
}
]
}
Сначала создаются ID, затем выполняется фильтрация.
Если поменять порядок:
{
"transform": [
{
"type": "filter",
"expr": "datum.value > 10"
},
{
"type": "identifier",
"as": "id"
}
]
}
идентификаторы будут назначены только оставшимся строкам.
После агрегации строки меняются.
Исходные данные:
[
{ "category": "A", "value": 10 },
{ "category": "A", "value": 20 }
]
После aggregate:
[
{ "category": "A", "sum": 30 }
]
Если identifier расположен до агрегации:
[
{ "category": "A", "value": 10, "id": 1 },
{ "category": "A", "value": 20, "id": 2 }
]
после aggregate старые ID теряются.
Поэтому обычно identifier размещают после операций,
изменяющих структуру набора данных.
Трансформация window создаёт порядковые значения, но они
не являются эквивалентом identifier.
{
"type": "window",
"ops": ["row_number"],
"as": ["row"]
}
{
"type": "identifier",
"as": "id"
}
Различия:
| window row_number | identifier |
|---|---|
| Может пересчитываться | Обычно стабилен |
| Зависит от сортировки | Не зависит |
| Может изменяться | Уникален |
| Используется для ранжирования | Используется для идентификации |
Внутри движка Vega используется специальный глобальный счётчик.
При обработке строки:
Упрощённая модель:
let counter = 0;
data.forEach(item => {
item.id = ++counter;
});
identifier зависит от порядка обработки.
Пример:
[
{ "name": "A" },
{ "name": "B" }
]
Результат:
[
{ "name": "A", "id": 1 },
{ "name": "B", "id": 2 }
]
После сортировки входного массива:
[
{ "name": "B" },
{ "name": "A" }
]
результат станет:
[
{ "name": "B", "id": 1 },
{ "name": "A", "id": 2 }
]
identifier не подходит для:
Причина: значения генерируются локально и не являются глобально стабильными.
Неверный подход:
{
"type": "identifier",
"as": "userId"
}
и последующее сохранение userId в базе.
После перезагрузки визуализации идентификаторы могут измениться.
В графовых визуализациях узлы должны иметь стабильные идентификаторы.
Пример:
{
"transform": [
{
"type": "identifier",
"as": "nodeId"
}
]
}
Это позволяет:
Даже полностью одинаковые строки получают разные значения.
Исходные данные:
[
{ "value": 10 },
{ "value": 10 },
{ "value": 10 }
]
Результат:
[
{ "value": 10, "id": 1 },
{ "value": 10, "id": 2 },
{ "value": 10, "id": 3 }
]
Это важно при визуализации плотных наборов данных.
Трансформация identifier относится к очень дешёвым
операциям.
Причины:
Сложность:
O(n)
где n — количество строк.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"data": [
{
"name": "table",
"values": [
{ "category": "A", "value": 28 },
{ "category": "B", "value": 55 },
{ "category": "C", "value": 43 }
],
"transform": [
{
"type": "identifier",
"as": "id"
}
]
}
],
"marks": [
{
"type": "rect",
"from": {
"data": "table"
},
"encode": {
"enter": {
"x": { "field": "id" },
"y": { "field": "value" }
}
}
}
]
}
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{ "year": 2020, "sales": 100 },
{ "year": 2021, "sales": 120 },
{ "year": 2022, "sales": 140 }
]
},
"transform": [
{
"identifier": "id"
}
],
"mark": "line",
"encoding": {
"x": {
"field": "year",
"type": "ordinal"
},
"y": {
"field": "sales",
"type": "quantitative"
},
"detail": {
"field": "id"
}
}
}
Часто используется совместно с вычисляемыми полями.
{
"transform": [
{
"type": "identifier",
"as": "id"
},
{
"type": "formula",
"expr": "datum.value * 100",
"as": "scaled"
}
]
}
Результат:
[
{
"value": 10,
"id": 1,
"scaled": 1000
}
]
collect сортирует данные.
{
"transform": [
{
"type": "collect",
"sort": {
"field": "value"
}
},
{
"type": "identifier",
"as": "id"
}
]
}
В этом случае идентификаторы будут соответствовать уже отсортированному набору.
Порядок трансформаций критически важен.
[
identifier,
filter,
aggregate
]
[
filter,
aggregate,
identifier
]
Во втором случае ID создаются только для итоговых агрегированных строк.
Каждая точка должна иметь уникальный ключ.
Новые записи постоянно появляются в потоке.
Объекты должны корректно сопоставляться между кадрами.
Необходимы внутренние ключи.
Нужно понимать, какие строки изменились.
identifier не предоставляет:
Трансформация предназначена исключительно для внутренних нужд визуализации.
550e8400-e29b-41d4-a716-446655440000
1
2
3
4
UUID подходит для:
identifier подходит для:
Рекомендуемый порядок:
filter → aggregate → lookup → identifier
identifier — исключительно технический
идентификатор.
При изменении порядка данных ID изменятся.
{
"type": "identifier",
"as": "_id"
}
уменьшает объём памяти при больших наборах данных.
identifier → aggregate
В большинстве случаев бессмысленно.
"id": 15
не должен использоваться вне визуализации.
Порядок строк влияет на значения.
Если данные уже содержат уникальный ключ, identifier
может быть избыточен.
Наибольшую ценность трансформация представляет в динамических визуализациях: