В библиотеке Vega-Lite интерактивность строится вокруг механизма
selection и системы преобразований данных
(transforms). Одним из ключевых инструментов становится
filter transform, позволяющий динамически изменять
отображаемый набор данных в зависимости от действий пользователя.
Фильтрация через выделение используется в следующих сценариях:
Главная идея заключается в том, что пользовательское выделение превращается в условие фильтрации данных.
Механизм состоит из трёх частей:
params)select)transform.filter)Схема работы:
Пользователь → selection → параметр → filter transform → обновление графика
Простейший пример связывает два графика:
const spec = {
data: {
url: "data/cars.json"
},
vconcat: [
{
params: [
{
name: "brush",
select: "interval"
}
],
mark: "point",
encoding: {
x: { field: "Horsepower", type: "quantitative" },
y: { field: "Miles_per_Gallon", type: "quantitative" }
}
},
{
transform: [
{
filter: { param: "brush" }
}
],
mark: "bar",
encoding: {
x: {
field: "Origin",
type: "nominal"
},
y: {
aggregate: "count"
}
}
}
]
};
Ключевой блок:
transform: [
{
filter: { param: "brush" }
}
]
означает:
Оставить только те строки данных,
которые попали в выделение brush
Если выделение пустое, поведение зависит от конфигурации selection.
Выделение диапазона.
select: "interval"
Подходит для:
Выделение отдельных элементов.
select: "point"
Используется для:
const spec = {
data: {
values: [
{ category: "A", value: 10 },
{ category: "B", value: 20 },
{ category: "C", value: 30 }
]
},
params: [
{
name: "selected",
select: "point"
}
],
hconcat: [
{
mark: "bar",
encoding: {
x: { field: "category", type: "nominal" },
y: { field: "value", type: "quantitative" },
color: {
condition: {
param: "selected",
value: "orange"
},
value: "steelblue"
}
}
},
{
transform: [
{
filter: { param: "selected" }
}
],
mark: "text",
encoding: {
text: { field: "category" }
}
}
]
};
Transform не изменяет источник данных.
Изменяется только поток данных внутри визуализации.
Порядок transforms имеет значение.
transform: [
{ filter: { param: "brush" } },
{ aggregate: [...] }
]
и
transform: [
{ aggregate: [...] },
{ filter: { param: "brush" } }
]
дают разные результаты.
Pipeline выполняется сверху вниз.
transform: [
{
filter: { param: "brush" }
},
{
aggregate: [
{
op: "count",
as: "count"
}
]
}
]
Агрегация выполняется только для выбранных данных.
transform: [
{
aggregate: [
{
op: "count",
as: "count"
}
]
},
{
filter: { param: "brush" }
}
]
Сначала агрегируются все данные, затем применяется фильтр.
Во многих случаях это бессмысленно или приводит к неожиданному результату.
По умолчанию пустое выделение трактуется как:
Все данные выбраны
Это поведение называется:
empty: "all"
params: [
{
name: "brush",
select: {
type: "interval"
}
}
]
Поведение по умолчанию.
params: [
{
name: "brush",
select: {
type: "interval"
}
}
],
transform: [
{
filter: {
param: "brush",
empty: false
}
}
]
Теперь без выделения график будет пустым.
Наиболее мощный сценарий — coordinated views.
Crossfilter — техника, при которой несколько графиков взаимно фильтруют друг друга.
const brush = {
name: "brush",
select: "interval"
};
const spec = {
data: {
url: "data/cars.json"
},
params: [brush],
hconcat: [
{
mark: "point",
encoding: {
x: {
field: "Horsepower",
type: "quantitative"
},
y: {
field: "Miles_per_Gallon",
type: "quantitative"
}
}
},
{
transform: [
{
filter: { param: "brush" }
}
],
mark: "bar",
encoding: {
x: {
field: "Origin",
type: "nominal"
},
y: {
aggregate: "count"
}
}
}
]
};
Один selection может управлять множеством представлений.
transform: [
{
filter: { param: "brush" }
}
]
может повторяться в любом количестве chart blocks.
Selection можно совмещать с обычными условиями filter.
transform: [
{
filter: {
and: [
{ param: "brush" },
"datum.Horsepower > 100"
]
}
}
]
Фильтр означает:
Элемент находится в выделении
И
Horsepower > 100
{
and: [
{ param: "brush" },
"datum.value > 10"
]
}
{
or: [
{ param: "a" },
{ param: "b" }
]
}
{
not: { param: "brush" }
}
Инверсия selection.
Полезна для:
transform: [
{
filter: {
not: { param: "brush" }
}
}
]
Показываются только элементы вне выделения.
Selection в Vega-Lite — не просто UI-событие.
Это полноценное логическое состояние визуализации.
Его можно использовать:
Очень распространённый паттерн:
condition → визуальное выделение
filter → изменение набора данных
params: [
{
name: "selected",
select: "point"
}
],
mark: "point",
encoding: {
color: {
condition: {
param: "selected",
value: "red"
},
value: "gray"
}
}
и:
transform: [
{
filter: { param: "selected" }
}
]
Первый график подсвечивает точки.
Второй показывает только выбранные данные.
Selection может быть привязан к легенде.
params: [
{
name: "origin",
select: {
type: "point",
fields: ["Origin"]
},
bind: "legend"
}
]
transform: [
{
filter: { param: "origin" }
}
]
Теперь легенда становится элементом управления фильтрацией.
Selection может учитывать несколько полей одновременно.
params: [
{
name: "sel",
select: {
type: "point",
fields: ["Origin", "Cylinders"]
}
}
]
Filter будет учитывать комбинацию полей.
Для scatter plot часто используется nearest selection.
select: {
type: "point",
nearest: true
}
Это позволяет выбирать ближайшую точку без точного попадания курсором.
Интерактивная фильтрация может становиться дорогой операцией.
Особенно при:
Лучше предварительно агрегировать данные.
Плохо:
transform: [
...
...
...
...
...
]
Каждый шаг увеличивает стоимость перерасчёта.
Для очень больших datasets:
Backend → агрегированные данные → Vega-Lite
вместо передачи миллионов строк.
Filter можно применять отдельно к слоям.
layer: [
{
mark: "point"
},
{
transform: [
{
filter: { param: "brush" }
}
],
mark: {
type: "point",
size: 200
}
}
]
Первый слой показывает все точки.
Второй — только выбранные.
Удаляет элементы из dataflow.
Элементы остаются в dataflow, но меняется отображение.
transform: [
{
filter: { param: "brush" }
}
]
Преимущества:
Недостаток:
opacity: {
condition: {
param: "brush",
value: 1
},
value: 0.1
}
Преимущества:
Недостаток:
Одно из самых важных применений selection filtering — динамические агрегаты.
transform: [
{
filter: { param: "brush" }
}
],
encoding: {
y: {
aggregate: "mean",
field: "Miles_per_Gallon"
}
}
Среднее значение пересчитывается в реальном времени только для выбранных записей.
Selection filter можно комбинировать с lookup transform.
transform: [
{
filter: { param: "selected" }
},
{
lookup: "id",
from: {
data: {
url: "details.json"
},
key: "id",
fields: ["description"]
}
}
]
Часто используется последовательность:
filter → calculate
transform: [
{
filter: { param: "brush" }
},
{
calculate: "datum.price * datum.quantity",
as: "total"
}
]
Вычисления выполняются только для выбранных данных.
Типичные проблемы:
Частая ошибка:
filter: { param: "brush" }
при:
name: "selection"
Имена должны совпадать.
Selection работает только в пределах допустимого scope.
Например:
vconcat
hconcat
layer
repeat
facet
могут влиять на доступность параметра.
При использовании repeat важно учитывать наследование selection.
repeat: {
column: ["Horsepower", "Acceleration"]
}
Selection может применяться ко всем повторяемым графикам одновременно.
Внутри Vega все transforms работают как реактивный поток данных.
Изменение selection автоматически:
Именно поэтому interactive filtering выглядит мгновенным даже при сложных визуализациях.
filter transform — центральный механизм интерактивной
аналитики в Vega-Lite.
Он превращает пользовательские действия в реактивные преобразования данных и позволяет строить: