У меня есть большой набор данных временных рядов, который при уменьшении масштаба позволяет просматривать интерактивный график. Кто-нибудь экспериментировал с динамическим уменьшением размера набора данных в зависимости от уровня масштабирования? Например, преобразование выборки, при котором номер выборки применяется к видимым данным, а не ко всему набору данных, но затем отключается за пределами определенного размера домена. Возможно ли такое?
Вот макет:
Вот как я сгенерировал данные:
dt2 = datetime.datetime.now()
ydt = datetime.timedelta(days=365)
dt1 = dt2 - ydt
dt1 = dt1.timestamp()
dt2 = dt2.timestamp()
times = [random.uniform(dt1, dt2) for i in range(10**5)]
times.sort()
values = []
for t in times:
d = {}
t = datetime.datetime.fromtimestamp(t)
t = t.isoformat(" ", timespec = "seconds")
v = random.uniform(0, 1000)
c = random.randint(0,1)
d["time"] = t
d["data"] = v
d["category"] = c
values.append(d)
with open("./test.json", "wt") as f:
json.dump(values[:10**4], f)
И вот чего мне удалось добиться в редакторе Vega (пришлось обрезать данные, чтобы они соответствовали ссылке).
Я попробую ответить на этот вопрос после того, как закончу решение моего предыдущего вопроса.
Сигналы официально не поддерживаются в VL, но их можно взломать. Здесь рассчитывается диапазон домена и в зависимости от значения делается выборка. Очевидно, вы можете расширить троичный оператор дополнительными условиями или придумать выражение для своей логики.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"time": "2023-08-31 15:12:40", "data": 265.1037232391961, "category": 1},
{"time": "2023-08-31 15:15:26", "data": 989.2391954577464, "category": 1},
{"time": "2023-08-31 15:15:29", "data": 426.3748533977788, "category": 0},
{"time": "2023-08-31 15:18:11", "data": 563.7725296786067, "category": 1},
{"time": "2023-08-31 15:21:25", "data": 322.4493083566362, "category": 0},
{"time": "2023-08-31 15:24:54", "data": 822.6771646740021, "category": 1},
{
"time": "2023-08-31 15:28:10",
"data": 294.37404484299054,
"category": 1
},
{"time": "2023-08-31 15:28:22", "data": 838.7462086185608, "category": 0},
{"time": "2023-08-31 15:35:58", "data": 961.3893188770259, "category": 0},
{
"time": "2023-08-31 15:36:07",
"data": 241.45631836625802,
"category": 0
},
{
"time": "2023-08-31 15:49:33",
"data": 191.96326506124362,
"category": 0
},
{
"time": "2023-08-31 15:51:15",
"data": 450.50733664623965,
"category": 1
},
{"time": "2023-08-31 15:56:35", "data": 390.5921971631632, "category": 1},
{"time": "2023-08-31 15:57:52", "data": 829.8364876130439, "category": 1},
{"time": "2023-08-31 16:01:06", "data": 996.0349700996576, "category": 0},
{"time": "2023-08-31 16:02:36", "data": 78.24722444300802, "category": 0},
{"time": "2023-08-31 16:14:05", "data": 942.3350040849994, "category": 0},
{"time": "2023-08-31 16:14:56", "data": 860.58714895142, "category": 1},
{"time": "2023-08-31 16:15:08", "data": 515.199102407516, "category": 1},
{
"time": "2023-08-31 16:23:20",
"data": 166.05721829849873,
"category": 1
},
{
"time": "2023-08-31 16:30:05",
"data": 439.73137493646266,
"category": 0
},
{"time": "2023-08-31 16:32:31", "data": 869.245076742056, "category": 0},
{
"time": "2023-08-31 16:35:48",
"data": 480.50968063008304,
"category": 1
},
{"time": "2023-08-31 16:37:50", "data": 476.877035209344, "category": 1},
{"time": "2023-08-31 16:39:36", "data": 733.3017448826324, "category": 0},
{"time": "2023-08-31 16:44:17", "data": 636.686519092496, "category": 1},
{"time": "2023-08-31 16:45:08", "data": 694.5261775005811, "category": 0},
{"time": "2023-08-31 16:51:36", "data": 695.7401884245502, "category": 0},
{"time": "2023-08-31 16:55:29", "data": 570.0935946720598, "category": 1},
{
"time": "2023-08-31 16:57:05",
"data": 277.22052647262717,
"category": 0
},
{
"time": "2023-08-31 16:58:27",
"data": 480.36926264607274,
"category": 1
},
{"time": "2023-08-31 17:02:34", "data": 893.3698570026319, "category": 1},
{
"time": "2023-08-31 17:05:32",
"data": 236.71895124154685,
"category": 1
},
{"time": "2023-08-31 17:08:46", "data": 573.0841835923452, "category": 0},
{"time": "2023-08-31 17:14:37", "data": 191.7254918774728, "category": 0},
{"time": "2023-08-31 17:16:43", "data": 94.93763899240804, "category": 1},
{"time": "2023-08-31 17:24:40", "data": 936.4038465823089, "category": 0},
{
"time": "2023-08-31 17:31:09",
"data": 390.84825100994567,
"category": 1
},
{"time": "2023-08-31 17:35:35", "data": 14.48187309843274, "category": 0},
{
"time": "2023-08-31 17:35:47",
"data": 443.05398617944593,
"category": 1
},
{"time": "2023-08-31 17:40:44", "data": 30.0828399028229, "category": 0},
{"time": "2023-08-31 17:48:33", "data": 768.0549896500464, "category": 1},
{"time": "2023-08-31 17:53:29", "data": 71.57068127924227, "category": 0},
{"time": "2023-08-31 18:04:56", "data": 594.7138236213322, "category": 0},
{"time": "2023-08-31 18:06:44", "data": 29.21370270526036, "category": 0},
{"time": "2023-08-31 18:28:22", "data": 852.7093808483378, "category": 1},
{"time": "2023-08-31 18:30:01", "data": 576.9728506525139, "category": 1},
{"time": "2023-08-31 18:31:41", "data": 968.1882202042807, "category": 1},
{"time": "2023-08-31 18:31:51", "data": 185.6873327854428, "category": 1},
{"time": "2023-08-31 18:33:31", "data": 258.211113709635, "category": 0},
{"time": "2023-08-31 18:36:36", "data": 641.264570256715, "category": 1},
{"time": "2023-08-31 18:39:52", "data": 717.6143367808544, "category": 1},
{"time": "2023-08-31 18:39:52", "data": 191.4611806426172, "category": 1},
{"time": "2023-08-31 18:41:38", "data": 136.9116350629923, "category": 0},
{"time": "2023-08-31 18:57:48", "data": 62.11343548023751, "category": 1},
{"time": "2023-08-31 18:58:26", "data": 529.5089127094398, "category": 0},
{
"time": "2023-08-31 19:07:54",
"data": 153.13269404700824,
"category": 1
},
{"time": "2023-08-31 19:09:17", "data": 705.4049459845114, "category": 0},
{
"time": "2023-08-31 19:11:07",
"data": 300.90132125121005,
"category": 1
},
{"time": "2023-08-31 19:20:25", "data": 946.4725291504993, "category": 1},
{
"time": "2023-08-31 19:23:48",
"data": 319.04133613813724,
"category": 1
},
{"time": "2023-08-31 19:24:25", "data": 464.2923297748929, "category": 1},
{"time": "2023-08-31 19:28:02", "data": 836.436678063193, "category": 1},
{"time": "2023-08-31 19:28:08", "data": 5.992853044164859, "category": 1},
{"time": "2023-08-31 19:40:01", "data": 873.6847072580948, "category": 1},
{"time": "2023-08-31 19:43:41", "data": 431.0286183407737, "category": 1},
{
"time": "2023-08-31 19:51:22",
"data": 396.43260404732825,
"category": 0
},
{"time": "2023-08-31 19:54:08", "data": 575.9715221353141, "category": 0},
{
"time": "2023-08-31 19:55:53",
"data": 44.016217670442614,
"category": 0
},
{"time": "2023-08-31 19:58:14", "data": 988.9639046666363, "category": 1},
{"time": "2023-08-31 20:05:53", "data": 742.2798696276691, "category": 0},
{"time": "2023-08-31 20:07:13", "data": 982.7119961613008, "category": 0},
{"time": "2023-08-31 20:15:20", "data": 976.3381077100345, "category": 1},
{"time": "2023-08-31 20:20:15", "data": 498.5276910780252, "category": 0},
{"time": "2023-08-31 20:22:29", "data": 301.4863894174468, "category": 1},
{
"time": "2023-08-31 20:31:03",
"data": 232.56452406666895,
"category": 1
},
{"time": "2023-08-31 20:33:52", "data": 694.171014904713, "category": 1},
{
"time": "2023-08-31 20:35:45",
"data": 102.79567934930212,
"category": 1
},
{
"time": "2023-08-31 20:47:32",
"data": 431.64822699883376,
"category": 1
},
{"time": "2023-08-31 20:55:19", "data": 683.217576875891, "category": 0},
{"time": "2023-08-31 20:55:36", "data": 879.5945045918183, "category": 1},
{"time": "2023-08-31 21:04:28", "data": 164.6834561802648, "category": 1},
{
"time": "2023-08-31 21:06:04",
"data": 22.588620229922583,
"category": 1
},
{"time": "2023-08-31 21:07:10", "data": 757.0796861192514, "category": 1},
{"time": "2023-08-31 21:23:43", "data": 848.456892794343, "category": 1},
{
"time": "2023-08-31 21:34:38",
"data": 447.89147371830785,
"category": 1
},
{"time": "2023-08-31 21:45:30", "data": 862.3116375036777, "category": 1},
{"time": "2023-08-31 21:47:00", "data": 967.0312319533795, "category": 0},
{"time": "2023-08-31 21:47:56", "data": 966.4938018703745, "category": 1},
{"time": "2023-08-31 21:49:45", "data": 890.2567189914545, "category": 0},
{
"time": "2023-08-31 21:55:40",
"data": 362.80312104639677,
"category": 1
},
{"time": "2023-08-31 21:58:55", "data": 834.7469369912607, "category": 1},
{"time": "2023-08-31 22:01:02", "data": 584.1447613550432, "category": 1},
{"time": "2023-08-31 22:01:06", "data": 82.66592460479994, "category": 1},
{
"time": "2023-08-31 22:02:00",
"data": 332.67959271479384,
"category": 0
},
{
"time": "2023-08-31 22:02:32",
"data": 316.51081491367347,
"category": 0
},
{
"time": "2023-08-31 22:08:31",
"data": 336.10098602094985,
"category": 1
},
{"time": "2023-08-31 22:18:52", "data": 873.7313013506864, "category": 0},
{
"time": "2023-08-31 22:19:24",
"data": 312.42947148514776,
"category": 1
},
{"time": "2023-08-31 22:28:48", "data": 582.8096654568776, "category": 1}
]
},
"params": [{"name": "test", "expr": "span(grid_time)/100000"}],
"transform": [
{"filter": "datum.data > 0"},
{"sample": {"signal": "test<130?100:10"}}
],
"title": "Too Much Data",
"config": {"font": "monospace"},
"width": "container",
"layer": [
{
"params": [
{
"name": "grid",
"bind": "scales",
"select": {
"type": "interval",
"encodings": ["x"],
"on": "[mousedown[!event.shiftKey], mouseup] > mousemove",
"translate": "[mousedown[!event.shiftKey], mouseup] > mousemove!"
}
},
{
"name": "brush",
"select": {
"type": "interval",
"encodings": ["x"],
"on": "[mousedown[event.shiftKey], mouseup] > mousemove",
"translate": "[mousedown[event.shiftKey], mouseup] > mousemove!"
}
}
],
"mark": "point",
"encoding": {
"x": {"field": "time", "type": "temporal"},
"y": {"field": "data", "type": "quantitative"},
"color": {"field": "category", "type": "nominal"}
}
},
{
"transform": [
{"filter": {"param": "brush"}},
{"aggregate": [{"op": "count", "as": "count"}]}
],
"mark": "text",
"encoding": {"text": {"field": "count", "type": "quantitative"}}
}
]
}
Хм, я подумывал о добавлении обработчика сигнала grid_time
JS и ручном удалении данных с помощью наборов изменений, но это намного проще, спасибо! Мне больше не нужно беспокоиться о том, безопасно ли изменять данные в обработчике и безопасно ли вызывать runAsync
или как это сделать insert
, сохраняя порядок. Только время покажет, достаточно ли преобразования sample
для сохранения формы моих данных, или мне нужен более сложный алгоритм прореживания кривой в JS.
Я хотел бы визуализировать параметр test
, который поможет написать выражение для моей логики — возможно ли это?
Отметьте это как решенное и задайте новый вопрос с примерами данных и точным новым требованием, поскольку я не на 100% понимаю, что вы имеете в виду.
Не нужно еще одного вопроса, я все понял. test
можно визуализировать на графике и в консоли как ... text: {expr: "warn(test)"} ...
, в отличие от записей данных, которые доступны в datum
или data('name')
для конкретных источников.
У меня есть эвристика, которая в среднем отображает около X
баллов: total_count_orig / (grid_count_orig / X)
. Источник данных orig
предварительно отсортирован, поэтому я использую эффективный двоичный поиск. Приятно видеть, как точки появляются и исчезают при масштабировании, но, к сожалению, при дальнейшем увеличении производительность становится несостоятельной. С X=400
и никогда не достигающим пика выше 425 (мои данные довольно регулярны), он быстро замедляется до ползания, даже когда я настолько увеличен, видно только 10-20 точек данных.
Как только я уменьшаю масштаб, производительность восстанавливается. Я подозреваю, что sample
выполняет повторную выборку с нуля всякий раз, когда меняется сигнал сетки/представления, и он становится очень медленным, когда ему приходится отфильтровывать почти все данные. Пожалуйста, дайте мне знать, если есть более эффективный подход!
VegaLite не предназначен для обработки огромного количества отдельных точек данных — данные обычно необходимо каким-то образом агрегировать перед их визуализацией. Вероятно, вам нужно посмотреть что-то вроде github.com/vega/vega-plus, которое может обрабатывать миллионы записей.
Как и в этом вопросе, мои данные довольно просты — кодировки x
(время), y
и color
. Он уже предварительно отфильтрован, поэтому я не использую никаких преобразований. Это заставляет меня думать, что такой проект, как VegaFusion, мне не поможет, но VegaPlus может быть излишним. Знаете ли вы какие-либо проекты Vega, которые могут осуществлять потоковую передачу из базы данных sqlite?
Я собираю данные за большой период времени, поэтому имеется около 50 000 строк (и я хочу объединить еще два графика), но я использую только простые метки в виде точек. Несмотря на 50 000 строк, меня интересует только отрисовка примерно 500 отметок за раз. Настоящая главная проблема это sample
. Это смехотворно медленно и перемешивает мои данные. Как вы думаете, стоит ли попробовать заменить его на filter
. Я бы создал ячейки X
и назначил каждой строке ячейку. Когда срабатывает сигнал представления/сетки, я бы вычислял текущий уровень масштабирования (из X
) и отфильтровывал все строки >= x
.
50 тысяч все еще довольно много, но да, я бы пошел по пути фильтра. Проблема в том, что при полном уменьшении масштаба вам также потребуется выполнить группировку, поскольку 50 тысяч точек, вероятно, слишком много для одновременного отображения. Отметьте этот вопрос как решенный и задайте новый вместе с попыткой, если вы все еще испытываете трудности.
У фильтра та же проблема: чем больше вы увеличиваете масштаб, тем медленнее становится VegaLite, даже если вы только панорамируете и панорамируете только 5-10 знаков. И, как sample
, он меняет порядок моих данных, хотя, по крайней мере, каждый отдельный контейнер остается отсортированным при повторном введении, что должно хорошо работать с Chrome timsort
.
Интересно.... Если вы сначала отфильтруете {param: grid}
, график при увеличении станет намного более отзывчивым. Очевидно, фильтруется весь набор данных, даже записи, находящиеся вне поля зрения, но фильтрация по предикату выбора оптимизирована гораздо лучше, поэтому последующим фильтрам нужно фильтровать только видимые данные.
Неважно... это просто изменило мою выборочную эвристику, сделав ее менее агрессивной (показать больше точек). Интересно, что отображение большего количества точек может быть значительно быстрее, чем агрессивная выборка.
Я просмотрел исходный код образца и считаю, что смогу улучшить его производительность для своего использования, хотя не уверен, что достаточно ли этого. Взгляните на VegaFusion или VegaPlus: это движки, которые переносят сложные вычисления на отдельный процессор базы данных. У меня их нет, поэтому я не уверен, что они отвечают моим потребностям. Конечно, потоковая передача данных будет вести себя как тот сэмплер, который мне нужен, но я не уверен, что оно того стоит. Как вы думаете, для моего конкретного использования мне следует сосредоточиться на специальном сэмплере или стороннем проекте Vega?
В любом случае, если вы знаете, как создать собственный vega.transform в vega-lite, я буду признателен.
Можете ли вы задать новый вопрос с полной рабочей спецификацией и всеми загруженными 50 тысячами точек данных. Возможно, вы сможете фильтровать и выполнять выборку, используя существующие преобразования, но тестирование невозможно без данных и спецификации. VL более ограничен, и с Vega у вас может быть больше возможностей.
Создайте суть своих данных, чтобы вы могли поделиться ссылкой, не слишком большой.
Это решено?