pond-ts 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LiveAggregation.d.ts +54 -0
- package/dist/LiveAggregation.d.ts.map +1 -0
- package/dist/LiveAggregation.js +240 -0
- package/dist/LiveAggregation.js.map +1 -0
- package/dist/LiveRollingAggregation.d.ts +43 -0
- package/dist/LiveRollingAggregation.d.ts.map +1 -0
- package/dist/LiveRollingAggregation.js +178 -0
- package/dist/LiveRollingAggregation.js.map +1 -0
- package/dist/LiveSeries.d.ts +63 -0
- package/dist/LiveSeries.d.ts.map +1 -0
- package/dist/LiveSeries.js +340 -0
- package/dist/LiveSeries.js.map +1 -0
- package/dist/LiveView.d.ts +59 -0
- package/dist/LiveView.d.ts.map +1 -0
- package/dist/LiveView.js +337 -0
- package/dist/LiveView.js.map +1 -0
- package/dist/Sequence.d.ts +2 -2
- package/dist/Sequence.d.ts.map +1 -1
- package/dist/Sequence.js +4 -57
- package/dist/Sequence.js.map +1 -1
- package/dist/TimeSeries.d.ts +133 -3
- package/dist/TimeSeries.d.ts.map +1 -1
- package/dist/TimeSeries.js +748 -444
- package/dist/TimeSeries.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/reducers/avg.d.ts +3 -0
- package/dist/reducers/avg.d.ts.map +1 -0
- package/dist/reducers/avg.js +45 -0
- package/dist/reducers/avg.js.map +1 -0
- package/dist/reducers/count.d.ts +3 -0
- package/dist/reducers/count.d.ts.map +1 -0
- package/dist/reducers/count.js +35 -0
- package/dist/reducers/count.js.map +1 -0
- package/dist/reducers/difference.d.ts +3 -0
- package/dist/reducers/difference.d.ts.map +1 -0
- package/dist/reducers/difference.js +48 -0
- package/dist/reducers/difference.js.map +1 -0
- package/dist/reducers/first.d.ts +3 -0
- package/dist/reducers/first.d.ts.map +1 -0
- package/dist/reducers/first.js +23 -0
- package/dist/reducers/first.js.map +1 -0
- package/dist/reducers/index.d.ts +4 -0
- package/dist/reducers/index.d.ts.map +1 -0
- package/dist/reducers/index.js +35 -0
- package/dist/reducers/index.js.map +1 -0
- package/dist/reducers/keep.d.ts +3 -0
- package/dist/reducers/keep.d.ts.map +1 -0
- package/dist/reducers/keep.js +56 -0
- package/dist/reducers/keep.js.map +1 -0
- package/dist/reducers/last.d.ts +3 -0
- package/dist/reducers/last.d.ts.map +1 -0
- package/dist/reducers/last.js +23 -0
- package/dist/reducers/last.js.map +1 -0
- package/dist/reducers/max.d.ts +3 -0
- package/dist/reducers/max.d.ts.map +1 -0
- package/dist/reducers/max.js +25 -0
- package/dist/reducers/max.js.map +1 -0
- package/dist/reducers/median.d.ts +3 -0
- package/dist/reducers/median.d.ts.map +1 -0
- package/dist/reducers/median.js +39 -0
- package/dist/reducers/median.js.map +1 -0
- package/dist/reducers/min.d.ts +3 -0
- package/dist/reducers/min.d.ts.map +1 -0
- package/dist/reducers/min.js +25 -0
- package/dist/reducers/min.js.map +1 -0
- package/dist/reducers/percentile.d.ts +5 -0
- package/dist/reducers/percentile.d.ts.map +1 -0
- package/dist/reducers/percentile.js +56 -0
- package/dist/reducers/percentile.js.map +1 -0
- package/dist/reducers/rolling.d.ts +15 -0
- package/dist/reducers/rolling.d.ts.map +1 -0
- package/dist/reducers/rolling.js +84 -0
- package/dist/reducers/rolling.js.map +1 -0
- package/dist/reducers/stdev.d.ts +3 -0
- package/dist/reducers/stdev.d.ts.map +1 -0
- package/dist/reducers/stdev.js +58 -0
- package/dist/reducers/stdev.js.map +1 -0
- package/dist/reducers/sum.d.ts +3 -0
- package/dist/reducers/sum.d.ts.map +1 -0
- package/dist/reducers/sum.js +35 -0
- package/dist/reducers/sum.js.map +1 -0
- package/dist/reducers/types.d.ts +58 -0
- package/dist/reducers/types.d.ts.map +1 -0
- package/dist/reducers/types.js +2 -0
- package/dist/reducers/types.js.map +1 -0
- package/dist/types.d.ts +35 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/duration.d.ts +3 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +25 -0
- package/dist/utils/duration.js.map +1 -0
- package/package.json +5 -2
- package/LICENSE +0 -21
- package/README.md +0 -415
package/dist/TimeSeries.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
var _a;
|
|
1
2
|
import { BoundedSequence } from './BoundedSequence.js';
|
|
2
3
|
import { parseTimestampString } from './calendar.js';
|
|
3
4
|
import { Interval } from './Interval.js';
|
|
4
5
|
import { Time } from './Time.js';
|
|
5
6
|
import { TimeRange } from './TimeRange.js';
|
|
7
|
+
import { Event } from './Event.js';
|
|
6
8
|
import { Sequence } from './Sequence.js';
|
|
7
9
|
import { validateAndNormalize } from './validate.js';
|
|
10
|
+
import { parseDuration } from './utils/duration.js';
|
|
11
|
+
import { resolveReducer, } from './reducers/index.js';
|
|
8
12
|
function isObjectRow(value) {
|
|
9
13
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
10
14
|
}
|
|
@@ -89,6 +93,23 @@ function parseJsonRows(schema, rows, options = {}) {
|
|
|
89
93
|
}));
|
|
90
94
|
});
|
|
91
95
|
}
|
|
96
|
+
function serializeJsonKey(kind, key, rowFormat) {
|
|
97
|
+
if (kind === 'time') {
|
|
98
|
+
return key.begin();
|
|
99
|
+
}
|
|
100
|
+
if (kind === 'timeRange') {
|
|
101
|
+
return rowFormat === 'object'
|
|
102
|
+
? { start: key.begin(), end: key.end() }
|
|
103
|
+
: [key.begin(), key.end()];
|
|
104
|
+
}
|
|
105
|
+
const interval = key;
|
|
106
|
+
return rowFormat === 'object'
|
|
107
|
+
? { value: interval.value, start: interval.begin(), end: interval.end() }
|
|
108
|
+
: [interval.value, interval.begin(), interval.end()];
|
|
109
|
+
}
|
|
110
|
+
function serializeJsonValue(value) {
|
|
111
|
+
return value === undefined ? null : value;
|
|
112
|
+
}
|
|
92
113
|
function toRows(schema, events) {
|
|
93
114
|
return events.map((event) => {
|
|
94
115
|
const data = event.data();
|
|
@@ -100,6 +121,20 @@ function toRows(schema, events) {
|
|
|
100
121
|
]);
|
|
101
122
|
});
|
|
102
123
|
}
|
|
124
|
+
function toObjects(schema, events) {
|
|
125
|
+
const keyColumn = schema[0];
|
|
126
|
+
const dataColumns = schema.slice(1);
|
|
127
|
+
return events.map((event) => {
|
|
128
|
+
const row = {
|
|
129
|
+
[keyColumn.name]: event.key(),
|
|
130
|
+
};
|
|
131
|
+
const data = event.data();
|
|
132
|
+
for (const column of dataColumns) {
|
|
133
|
+
row[column.name] = data[column.name];
|
|
134
|
+
}
|
|
135
|
+
return Object.freeze(row);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
103
138
|
function isEventKey(value) {
|
|
104
139
|
return (typeof value === 'object' &&
|
|
105
140
|
value !== null &&
|
|
@@ -316,278 +351,60 @@ function bucketOverlapsHalfOpen(bucket, event) {
|
|
|
316
351
|
function aggregateValues(operation, values) {
|
|
317
352
|
const defined = values.filter((value) => value !== undefined);
|
|
318
353
|
const numeric = defined.filter((value) => typeof value === 'number');
|
|
319
|
-
|
|
320
|
-
case 'count':
|
|
321
|
-
return defined.length;
|
|
322
|
-
case 'sum':
|
|
323
|
-
return numeric.reduce((sum, value) => sum + value, 0);
|
|
324
|
-
case 'avg':
|
|
325
|
-
return numeric.length === 0
|
|
326
|
-
? undefined
|
|
327
|
-
: numeric.reduce((sum, value) => sum + value, 0) / numeric.length;
|
|
328
|
-
case 'min':
|
|
329
|
-
return numeric.length === 0
|
|
330
|
-
? undefined
|
|
331
|
-
: numeric.reduce((left, right) => (left <= right ? left : right));
|
|
332
|
-
case 'max':
|
|
333
|
-
return numeric.length === 0
|
|
334
|
-
? undefined
|
|
335
|
-
: numeric.reduce((left, right) => (left >= right ? left : right));
|
|
336
|
-
case 'first':
|
|
337
|
-
return defined[0];
|
|
338
|
-
case 'last':
|
|
339
|
-
return defined[defined.length - 1];
|
|
340
|
-
}
|
|
354
|
+
return resolveReducer(operation).reduce(defined, numeric);
|
|
341
355
|
}
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
let definedCount = 0;
|
|
345
|
-
return {
|
|
346
|
-
add(value) {
|
|
347
|
-
if (value !== undefined) {
|
|
348
|
-
definedCount += 1;
|
|
349
|
-
}
|
|
350
|
-
},
|
|
351
|
-
snapshot() {
|
|
352
|
-
return definedCount;
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
if (operation === 'sum') {
|
|
357
|
-
let numericSum = 0;
|
|
358
|
-
return {
|
|
359
|
-
add(value) {
|
|
360
|
-
if (typeof value === 'number') {
|
|
361
|
-
numericSum += value;
|
|
362
|
-
}
|
|
363
|
-
},
|
|
364
|
-
snapshot() {
|
|
365
|
-
return numericSum;
|
|
366
|
-
},
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
if (operation === 'avg') {
|
|
370
|
-
let numericSum = 0;
|
|
371
|
-
let numericCount = 0;
|
|
372
|
-
return {
|
|
373
|
-
add(value) {
|
|
374
|
-
if (typeof value === 'number') {
|
|
375
|
-
numericSum += value;
|
|
376
|
-
numericCount += 1;
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
snapshot() {
|
|
380
|
-
return numericCount === 0 ? undefined : numericSum / numericCount;
|
|
381
|
-
},
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
if (operation === 'min') {
|
|
385
|
-
let numericValue;
|
|
386
|
-
return {
|
|
387
|
-
add(value) {
|
|
388
|
-
if (typeof value !== 'number') {
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
numericValue =
|
|
392
|
-
numericValue === undefined || value < numericValue
|
|
393
|
-
? value
|
|
394
|
-
: numericValue;
|
|
395
|
-
},
|
|
396
|
-
snapshot() {
|
|
397
|
-
return numericValue;
|
|
398
|
-
},
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
if (operation === 'max') {
|
|
402
|
-
let numericValue;
|
|
403
|
-
return {
|
|
404
|
-
add(value) {
|
|
405
|
-
if (typeof value !== 'number') {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
numericValue =
|
|
409
|
-
numericValue === undefined || value > numericValue
|
|
410
|
-
? value
|
|
411
|
-
: numericValue;
|
|
412
|
-
},
|
|
413
|
-
snapshot() {
|
|
414
|
-
return numericValue;
|
|
415
|
-
},
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
if (operation === 'first') {
|
|
419
|
-
let firstValue;
|
|
420
|
-
return {
|
|
421
|
-
add(value) {
|
|
422
|
-
if (firstValue === undefined && value !== undefined) {
|
|
423
|
-
firstValue = value;
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
snapshot() {
|
|
427
|
-
return firstValue;
|
|
428
|
-
},
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
if (operation === 'last') {
|
|
432
|
-
let lastValue;
|
|
433
|
-
return {
|
|
434
|
-
add(value) {
|
|
435
|
-
if (value !== undefined) {
|
|
436
|
-
lastValue = value;
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
snapshot() {
|
|
440
|
-
return lastValue;
|
|
441
|
-
},
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
throw new TypeError(`unsupported aggregate reducer: ${operation}`);
|
|
356
|
+
function isBuiltInAggregateReducer(reducer) {
|
|
357
|
+
return typeof reducer === 'string';
|
|
445
358
|
}
|
|
446
|
-
function
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
return [entries, head];
|
|
452
|
-
};
|
|
453
|
-
if (operation === 'count') {
|
|
454
|
-
let definedCount = 0;
|
|
455
|
-
return {
|
|
456
|
-
add(_index, value) {
|
|
457
|
-
if (value !== undefined) {
|
|
458
|
-
definedCount += 1;
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
remove(_index, value) {
|
|
462
|
-
if (value !== undefined) {
|
|
463
|
-
definedCount -= 1;
|
|
464
|
-
}
|
|
465
|
-
},
|
|
466
|
-
snapshot() {
|
|
467
|
-
return definedCount;
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
if (operation === 'sum') {
|
|
472
|
-
let numericSum = 0;
|
|
473
|
-
return {
|
|
474
|
-
add(_index, value) {
|
|
475
|
-
if (typeof value === 'number') {
|
|
476
|
-
numericSum += value;
|
|
477
|
-
}
|
|
478
|
-
},
|
|
479
|
-
remove(_index, value) {
|
|
480
|
-
if (typeof value === 'number') {
|
|
481
|
-
numericSum -= value;
|
|
482
|
-
}
|
|
483
|
-
},
|
|
484
|
-
snapshot() {
|
|
485
|
-
return numericSum;
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
if (operation === 'avg') {
|
|
490
|
-
let numericSum = 0;
|
|
491
|
-
let numericCount = 0;
|
|
492
|
-
return {
|
|
493
|
-
add(_index, value) {
|
|
494
|
-
if (typeof value === 'number') {
|
|
495
|
-
numericSum += value;
|
|
496
|
-
numericCount += 1;
|
|
497
|
-
}
|
|
498
|
-
},
|
|
499
|
-
remove(_index, value) {
|
|
500
|
-
if (typeof value === 'number') {
|
|
501
|
-
numericSum -= value;
|
|
502
|
-
numericCount -= 1;
|
|
503
|
-
}
|
|
504
|
-
},
|
|
505
|
-
snapshot() {
|
|
506
|
-
return numericCount === 0 ? undefined : numericSum / numericCount;
|
|
507
|
-
},
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
if (operation === 'min' || operation === 'max') {
|
|
511
|
-
let entries = [];
|
|
512
|
-
let head = 0;
|
|
513
|
-
return {
|
|
514
|
-
add(index, value) {
|
|
515
|
-
if (typeof value !== 'number') {
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
while (entries.length > head) {
|
|
519
|
-
const last = entries[entries.length - 1];
|
|
520
|
-
if (operation === 'min' ? last.value <= value : last.value >= value) {
|
|
521
|
-
break;
|
|
522
|
-
}
|
|
523
|
-
entries.pop();
|
|
524
|
-
}
|
|
525
|
-
entries.push({ index, value });
|
|
526
|
-
},
|
|
527
|
-
remove(index, value) {
|
|
528
|
-
if (typeof value !== 'number') {
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
if (entries[head]?.index === index) {
|
|
532
|
-
head += 1;
|
|
533
|
-
[entries, head] = compact(entries, head);
|
|
534
|
-
}
|
|
535
|
-
},
|
|
536
|
-
snapshot() {
|
|
537
|
-
return entries[head]?.value;
|
|
538
|
-
},
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
if (operation === 'first' || operation === 'last') {
|
|
542
|
-
let entries = [];
|
|
543
|
-
let head = 0;
|
|
544
|
-
return {
|
|
545
|
-
add(index, value) {
|
|
546
|
-
if (value !== undefined) {
|
|
547
|
-
entries.push({ index, value });
|
|
548
|
-
}
|
|
549
|
-
},
|
|
550
|
-
remove(index, value) {
|
|
551
|
-
if (value === undefined) {
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
if (entries[head]?.index === index) {
|
|
555
|
-
head += 1;
|
|
556
|
-
[entries, head] = compact(entries, head);
|
|
557
|
-
}
|
|
558
|
-
},
|
|
559
|
-
snapshot() {
|
|
560
|
-
return operation === 'first'
|
|
561
|
-
? entries[head]?.value
|
|
562
|
-
: entries[entries.length - 1]?.value;
|
|
563
|
-
},
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
throw new TypeError(`unsupported rolling reducer: ${operation}`);
|
|
359
|
+
function applyAggregateReducer(reducer, values) {
|
|
360
|
+
return isBuiltInAggregateReducer(reducer)
|
|
361
|
+
? aggregateValues(reducer, values)
|
|
362
|
+
: reducer(values);
|
|
567
363
|
}
|
|
568
|
-
function
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
364
|
+
function isAggregateOutputSpec(value) {
|
|
365
|
+
return (typeof value === 'object' &&
|
|
366
|
+
value !== null &&
|
|
367
|
+
'from' in value &&
|
|
368
|
+
'using' in value);
|
|
369
|
+
}
|
|
370
|
+
function normalizeAggregateColumns(schema, mapping) {
|
|
371
|
+
const columnsByName = new Map(schema.slice(1).map((column) => [column.name, column]));
|
|
372
|
+
const normalized = [];
|
|
373
|
+
for (const [outputName, raw] of Object.entries(mapping)) {
|
|
374
|
+
const sourceName = isAggregateOutputSpec(raw) ? raw.from : outputName;
|
|
375
|
+
const sourceColumn = columnsByName.get(sourceName);
|
|
376
|
+
if (!sourceColumn) {
|
|
377
|
+
throw new TypeError(`aggregate mapping references unknown source column '${sourceName}'`);
|
|
572
378
|
}
|
|
573
|
-
|
|
379
|
+
if (sourceColumn.kind !== 'number' &&
|
|
380
|
+
sourceColumn.kind !== 'string' &&
|
|
381
|
+
sourceColumn.kind !== 'boolean') {
|
|
382
|
+
throw new TypeError(`aggregate source column '${sourceName}' must be a scalar value column`);
|
|
383
|
+
}
|
|
384
|
+
const reducer = isAggregateOutputSpec(raw) ? raw.using : raw;
|
|
385
|
+
if (typeof reducer !== 'string' && typeof reducer !== 'function') {
|
|
386
|
+
throw new TypeError(`aggregate reducer for '${outputName}' must be a built-in name or function`);
|
|
387
|
+
}
|
|
388
|
+
const explicitKind = isAggregateOutputSpec(raw) ? raw.kind : undefined;
|
|
389
|
+
const resolvedKind = explicitKind ??
|
|
390
|
+
(typeof reducer === 'string' &&
|
|
391
|
+
resolveReducer(reducer).outputKind === 'number'
|
|
392
|
+
? 'number'
|
|
393
|
+
: sourceColumn.kind);
|
|
394
|
+
normalized.push({
|
|
395
|
+
output: outputName,
|
|
396
|
+
source: sourceName,
|
|
397
|
+
reducer,
|
|
398
|
+
kind: resolvedKind,
|
|
399
|
+
});
|
|
574
400
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
? 1
|
|
583
|
-
: unit === 's'
|
|
584
|
-
? 1_000
|
|
585
|
-
: unit === 'm'
|
|
586
|
-
? 60_000
|
|
587
|
-
: unit === 'h'
|
|
588
|
-
? 3_600_000
|
|
589
|
-
: 86_400_000;
|
|
590
|
-
return amount * multiplier;
|
|
401
|
+
return normalized;
|
|
402
|
+
}
|
|
403
|
+
function createAggregateBucketState(operation) {
|
|
404
|
+
return resolveReducer(operation).bucketState();
|
|
405
|
+
}
|
|
406
|
+
function createRollingReducerState(operation) {
|
|
407
|
+
return resolveReducer(operation).rollingState();
|
|
591
408
|
}
|
|
592
409
|
function duplicateValueColumnNames(schemas) {
|
|
593
410
|
const counts = new Map();
|
|
@@ -697,7 +514,7 @@ export class TimeSeries {
|
|
|
697
514
|
* the supplied `parse.timeZone`, which defaults to `UTC`.
|
|
698
515
|
*/
|
|
699
516
|
static fromJSON(input) {
|
|
700
|
-
return new
|
|
517
|
+
return new _a({
|
|
701
518
|
name: input.name,
|
|
702
519
|
schema: input.schema,
|
|
703
520
|
rows: parseJsonRows(input.schema, input.rows, input.parse),
|
|
@@ -710,6 +527,48 @@ export class TimeSeries {
|
|
|
710
527
|
this.events = validateAndNormalize(input);
|
|
711
528
|
Object.freeze(this);
|
|
712
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Example: `series.toJSON({ rowFormat: "object" })`.
|
|
532
|
+
* Serializes the series into the JSON-friendly shape accepted by `TimeSeries.fromJSON(...)`.
|
|
533
|
+
*
|
|
534
|
+
* Timestamps are emitted as numbers to avoid time zone ambiguity. Missing payload values are
|
|
535
|
+
* emitted as `null`. By default rows are emitted as arrays; use `rowFormat: "object"` for rows
|
|
536
|
+
* keyed by schema column names.
|
|
537
|
+
*/
|
|
538
|
+
toJSON(options = {}) {
|
|
539
|
+
const rowFormat = options.rowFormat ?? 'array';
|
|
540
|
+
const dataColumns = this.schema.slice(1);
|
|
541
|
+
if (rowFormat === 'object') {
|
|
542
|
+
const keyColumn = this.schema[0];
|
|
543
|
+
const rows = this.events.map((event) => {
|
|
544
|
+
const row = {
|
|
545
|
+
[keyColumn.name]: serializeJsonKey(keyColumn.kind, event.key(), rowFormat),
|
|
546
|
+
};
|
|
547
|
+
const data = event.data();
|
|
548
|
+
for (const column of dataColumns) {
|
|
549
|
+
row[column.name] = serializeJsonValue(data[column.name]);
|
|
550
|
+
}
|
|
551
|
+
return Object.freeze(row);
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
name: this.name,
|
|
555
|
+
schema: this.schema,
|
|
556
|
+
rows,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
const rows = this.events.map((event) => {
|
|
560
|
+
const data = event.data();
|
|
561
|
+
return Object.freeze([
|
|
562
|
+
serializeJsonKey(this.schema[0].kind, event.key(), rowFormat),
|
|
563
|
+
...dataColumns.map((column) => serializeJsonValue(data[column.name])),
|
|
564
|
+
]);
|
|
565
|
+
});
|
|
566
|
+
return {
|
|
567
|
+
name: this.name,
|
|
568
|
+
schema: this.schema,
|
|
569
|
+
rows,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
713
572
|
/**
|
|
714
573
|
* Builds a series from event data that has already been validated and ordered by the caller.
|
|
715
574
|
*
|
|
@@ -717,7 +576,7 @@ export class TimeSeries {
|
|
|
717
576
|
* order and normalized key invariants.
|
|
718
577
|
*/
|
|
719
578
|
static #fromTrustedEvents(name, schema, events) {
|
|
720
|
-
const series = Object.create(
|
|
579
|
+
const series = Object.create(_a.prototype);
|
|
721
580
|
series.name = name;
|
|
722
581
|
series.schema = Object.freeze(schema.slice());
|
|
723
582
|
series.events = Object.freeze(events.slice());
|
|
@@ -731,6 +590,14 @@ export class TimeSeries {
|
|
|
731
590
|
get rows() {
|
|
732
591
|
return toRows(this.schema, this.events);
|
|
733
592
|
}
|
|
593
|
+
/** Example: `series.toRows()`. Returns normalized row arrays using `Time`/`TimeRange`/`Interval` keys and `undefined` for missing payload values. */
|
|
594
|
+
toRows() {
|
|
595
|
+
return this.rows;
|
|
596
|
+
}
|
|
597
|
+
/** Example: `series.toObjects()`. Returns normalized schema-keyed object rows using temporal key objects and `undefined` for missing payload values. */
|
|
598
|
+
toObjects() {
|
|
599
|
+
return toObjects(this.schema, this.events);
|
|
600
|
+
}
|
|
734
601
|
/** Example: `series.at(0)`. Returns the event at the supplied zero-based position, if present. */
|
|
735
602
|
at(index) {
|
|
736
603
|
return this.events[index];
|
|
@@ -748,7 +615,7 @@ export class TimeSeries {
|
|
|
748
615
|
/** Example: `series.map(nextSchema, event => event)`. Maps each event into a new typed schema and returns a new series. */
|
|
749
616
|
map(schema, mapper) {
|
|
750
617
|
const mappedEvents = this.events.map((event, index) => mapper(event, index));
|
|
751
|
-
return new
|
|
618
|
+
return new _a({
|
|
752
619
|
name: this.name,
|
|
753
620
|
schema,
|
|
754
621
|
rows: toRows(schema, mappedEvents),
|
|
@@ -762,9 +629,9 @@ export class TimeSeries {
|
|
|
762
629
|
]);
|
|
763
630
|
const resultEvents = this.events.map((event) => event.asTime(options));
|
|
764
631
|
if ((options.at ?? 'begin') === 'begin') {
|
|
765
|
-
return
|
|
632
|
+
return _a.#fromTrustedEvents(this.name, schema, resultEvents);
|
|
766
633
|
}
|
|
767
|
-
return new
|
|
634
|
+
return new _a({
|
|
768
635
|
name: this.name,
|
|
769
636
|
schema,
|
|
770
637
|
rows: toRows(schema, resultEvents),
|
|
@@ -777,7 +644,7 @@ export class TimeSeries {
|
|
|
777
644
|
...this.schema.slice(1),
|
|
778
645
|
]);
|
|
779
646
|
const resultEvents = this.events.map((event) => event.asTimeRange());
|
|
780
|
-
return
|
|
647
|
+
return _a.#fromTrustedEvents(this.name, schema, resultEvents);
|
|
781
648
|
}
|
|
782
649
|
asInterval(value) {
|
|
783
650
|
const schema = Object.freeze([
|
|
@@ -789,10 +656,13 @@ export class TimeSeries {
|
|
|
789
656
|
? event.asInterval(() => value(event, index))
|
|
790
657
|
: event.asInterval(value);
|
|
791
658
|
});
|
|
792
|
-
return
|
|
659
|
+
return _a.#fromTrustedEvents(this.name, schema, nextEvents);
|
|
793
660
|
}
|
|
794
661
|
join(other, options = {}) {
|
|
795
|
-
const [left, right] = prepareSeriesForJoin([
|
|
662
|
+
const [left, right] = prepareSeriesForJoin([
|
|
663
|
+
this,
|
|
664
|
+
other,
|
|
665
|
+
], options);
|
|
796
666
|
const joinType = options.type ?? 'outer';
|
|
797
667
|
if (left.firstColumnKind !== right.firstColumnKind) {
|
|
798
668
|
throw new TypeError('cannot join series with different key kinds');
|
|
@@ -845,7 +715,7 @@ export class TimeSeries {
|
|
|
845
715
|
rightIndex += 1;
|
|
846
716
|
}
|
|
847
717
|
}
|
|
848
|
-
return
|
|
718
|
+
return _a.#fromTrustedEvents(left.name, resultSchema, joinedEvents);
|
|
849
719
|
}
|
|
850
720
|
/**
|
|
851
721
|
* Example: `series.align(Sequence.every("1m"))`.
|
|
@@ -877,7 +747,7 @@ export class TimeSeries {
|
|
|
877
747
|
const range = options.range ?? this.timeRange();
|
|
878
748
|
const resultSchema = makeAlignedSchema(this.schema);
|
|
879
749
|
if (!range) {
|
|
880
|
-
return new
|
|
750
|
+
return new _a({
|
|
881
751
|
name: this.name,
|
|
882
752
|
schema: resultSchema,
|
|
883
753
|
rows: [],
|
|
@@ -896,7 +766,7 @@ export class TimeSeries {
|
|
|
896
766
|
for (let i = 0; i < intervals.length; i += 1) {
|
|
897
767
|
const interval = intervals[i];
|
|
898
768
|
const t = sampleTime(interval, sample);
|
|
899
|
-
const data =
|
|
769
|
+
const data = alignLinearAt(this, t, valueColumns, cursor);
|
|
900
770
|
const row = new Array(resultColumns.length + 1);
|
|
901
771
|
row[0] = interval;
|
|
902
772
|
for (let j = 0; j < resultColumns.length; j += 1) {
|
|
@@ -909,7 +779,7 @@ export class TimeSeries {
|
|
|
909
779
|
})()
|
|
910
780
|
: intervals.map((interval) => {
|
|
911
781
|
const t = sampleTime(interval, sample);
|
|
912
|
-
const data =
|
|
782
|
+
const data = alignHoldAt(this, t);
|
|
913
783
|
return Object.freeze([
|
|
914
784
|
interval,
|
|
915
785
|
...resultSchema
|
|
@@ -917,126 +787,452 @@ export class TimeSeries {
|
|
|
917
787
|
.map((column) => data[column.name]),
|
|
918
788
|
]);
|
|
919
789
|
});
|
|
920
|
-
return new
|
|
790
|
+
return new _a({
|
|
921
791
|
name: this.name,
|
|
922
792
|
schema: resultSchema,
|
|
923
793
|
rows: alignedRows,
|
|
924
794
|
});
|
|
925
795
|
}
|
|
796
|
+
aggregate(sequence, mapping, options = {}) {
|
|
797
|
+
return aggregateInternal(this, sequence, mapping, options);
|
|
798
|
+
}
|
|
799
|
+
reduce(columnOrMapping, reducer) {
|
|
800
|
+
if (typeof columnOrMapping === 'string') {
|
|
801
|
+
const values = this.events.map((event) => {
|
|
802
|
+
const data = event.data();
|
|
803
|
+
return data[columnOrMapping];
|
|
804
|
+
});
|
|
805
|
+
return applyAggregateReducer(reducer, values);
|
|
806
|
+
}
|
|
807
|
+
const columns = normalizeAggregateColumns(this.schema, columnOrMapping);
|
|
808
|
+
const result = {};
|
|
809
|
+
for (const col of columns) {
|
|
810
|
+
const values = this.events.map((event) => {
|
|
811
|
+
const data = event.data();
|
|
812
|
+
return data[col.source];
|
|
813
|
+
});
|
|
814
|
+
result[col.output] = applyAggregateReducer(col.reducer, values);
|
|
815
|
+
}
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
groupBy(column, transform) {
|
|
819
|
+
const buckets = new Map();
|
|
820
|
+
for (const event of this.events) {
|
|
821
|
+
const raw = event.data()[column];
|
|
822
|
+
const key = raw === undefined ? 'undefined' : String(raw);
|
|
823
|
+
let bucket = buckets.get(key);
|
|
824
|
+
if (!bucket) {
|
|
825
|
+
bucket = [];
|
|
826
|
+
buckets.set(key, bucket);
|
|
827
|
+
}
|
|
828
|
+
bucket.push(event);
|
|
829
|
+
}
|
|
830
|
+
const buildGroup = (events) => new _a({
|
|
831
|
+
name: this.name,
|
|
832
|
+
schema: this.schema,
|
|
833
|
+
rows: toRows(this.schema, events),
|
|
834
|
+
});
|
|
835
|
+
if (transform) {
|
|
836
|
+
const result = new Map();
|
|
837
|
+
for (const [key, events] of buckets) {
|
|
838
|
+
result.set(key, transform(buildGroup(events), key));
|
|
839
|
+
}
|
|
840
|
+
return result;
|
|
841
|
+
}
|
|
842
|
+
const result = new Map();
|
|
843
|
+
for (const [key, events] of buckets) {
|
|
844
|
+
result.set(key, buildGroup(events));
|
|
845
|
+
}
|
|
846
|
+
return result;
|
|
847
|
+
}
|
|
926
848
|
/**
|
|
927
|
-
* Example: `series.
|
|
928
|
-
*
|
|
849
|
+
* Example: `series.diff("requests")`.
|
|
850
|
+
* Computes per-event differences for the specified numeric columns.
|
|
851
|
+
* Non-specified columns pass through unchanged. The first event gets
|
|
852
|
+
* `undefined` in affected columns unless `{ drop: true }` is passed,
|
|
853
|
+
* which removes the first event entirely.
|
|
929
854
|
*
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
* overlap under half-open overlap rules.
|
|
855
|
+
* Example: `series.diff(["requests", "cpu"])`.
|
|
856
|
+
* Multiple columns can be diffed in a single call.
|
|
933
857
|
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
858
|
+
* Example: `series.diff("requests", { drop: true })`.
|
|
859
|
+
* Drops the first event instead of keeping it with undefined values.
|
|
860
|
+
*/
|
|
861
|
+
diff(columns, options) {
|
|
862
|
+
return this.#diffOrRate('diff', columns, options);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Example: `series.rate("requests")`.
|
|
866
|
+
* Computes the per-second rate of change for the specified numeric columns.
|
|
867
|
+
* Non-specified columns pass through unchanged. The first event gets
|
|
868
|
+
* `undefined` in affected columns unless `{ drop: true }` is passed,
|
|
869
|
+
* which removes the first event entirely.
|
|
870
|
+
*
|
|
871
|
+
* Example: `series.rate(["requests", "cpu"])`.
|
|
872
|
+
* Multiple columns can be rated in a single call.
|
|
936
873
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
|
|
940
|
-
|
|
874
|
+
* Example: `series.rate("requests", { drop: true })`.
|
|
875
|
+
* Drops the first event instead of keeping it with undefined values.
|
|
876
|
+
*/
|
|
877
|
+
rate(columns, options) {
|
|
878
|
+
return this.#diffOrRate('rate', columns, options);
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Example: `series.pctChange("requests")`.
|
|
882
|
+
* Computes the percentage change `(curr - prev) / prev` for the specified
|
|
883
|
+
* numeric columns. Non-specified columns pass through unchanged. The first
|
|
884
|
+
* event gets `undefined` in affected columns unless `{ drop: true }` is
|
|
885
|
+
* passed.
|
|
886
|
+
*/
|
|
887
|
+
pctChange(columns, options) {
|
|
888
|
+
return this.#diffOrRate('pctChange', columns, options);
|
|
889
|
+
}
|
|
890
|
+
#diffOrRate(mode, columns, options) {
|
|
891
|
+
const cols = typeof columns === 'string' ? [columns] : columns;
|
|
892
|
+
const drop = options?.drop === true;
|
|
893
|
+
if (cols.length === 0) {
|
|
894
|
+
throw new Error(`${mode}() requires at least one column name`);
|
|
895
|
+
}
|
|
896
|
+
const targetSet = new Set(cols);
|
|
897
|
+
const outSchema = Object.freeze(this.schema.map((col, i) => {
|
|
898
|
+
if (i === 0)
|
|
899
|
+
return col;
|
|
900
|
+
if (targetSet.has(col.name)) {
|
|
901
|
+
return { ...col, kind: 'number', required: false };
|
|
902
|
+
}
|
|
903
|
+
return col;
|
|
904
|
+
}));
|
|
905
|
+
const events = this.events;
|
|
906
|
+
if (events.length === 0) {
|
|
907
|
+
return _a.#fromTrustedEvents(this.name, outSchema, []);
|
|
908
|
+
}
|
|
909
|
+
const resultEvents = [];
|
|
910
|
+
if (!drop) {
|
|
911
|
+
const firstData = { ...events[0].data() };
|
|
912
|
+
for (const col of cols) {
|
|
913
|
+
firstData[col] = undefined;
|
|
914
|
+
}
|
|
915
|
+
resultEvents.push(new Event(events[0].key(), firstData));
|
|
916
|
+
}
|
|
917
|
+
for (let i = 1; i < events.length; i++) {
|
|
918
|
+
const prev = events[i - 1];
|
|
919
|
+
const curr = events[i];
|
|
920
|
+
const data = { ...curr.data() };
|
|
921
|
+
const dt = mode === 'rate' ? (curr.begin() - prev.begin()) / 1000 : undefined;
|
|
922
|
+
for (const col of cols) {
|
|
923
|
+
const prevVal = prev.data()[col];
|
|
924
|
+
const currVal = data[col];
|
|
925
|
+
if (typeof currVal === 'number' && typeof prevVal === 'number') {
|
|
926
|
+
const delta = currVal - prevVal;
|
|
927
|
+
if (mode === 'pctChange') {
|
|
928
|
+
data[col] = prevVal !== 0 ? delta / prevVal : undefined;
|
|
929
|
+
}
|
|
930
|
+
else if (mode === 'rate') {
|
|
931
|
+
data[col] = dt !== 0 ? delta / dt : undefined;
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
data[col] = delta;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
data[col] = undefined;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
resultEvents.push(new Event(curr.key(), data));
|
|
942
|
+
}
|
|
943
|
+
return _a.#fromTrustedEvents(this.name, outSchema, resultEvents);
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Example: `series.cumulative({ requests: "sum" })`.
|
|
947
|
+
* Computes running accumulations for the specified numeric columns.
|
|
948
|
+
* Non-accumulated columns pass through unchanged.
|
|
941
949
|
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
950
|
+
* Built-in accumulators: `"sum"`, `"max"`, `"min"`, `"count"`.
|
|
951
|
+
* Custom accumulators: `(acc: number, value: number) => number`.
|
|
952
|
+
*/
|
|
953
|
+
cumulative(spec) {
|
|
954
|
+
const entries = Object.entries(spec);
|
|
955
|
+
if (entries.length === 0) {
|
|
956
|
+
throw new Error('cumulative() requires at least one column');
|
|
957
|
+
}
|
|
958
|
+
const targetSet = new Set(entries.map(([name]) => name));
|
|
959
|
+
const outSchema = Object.freeze(this.schema.map((col, i) => {
|
|
960
|
+
if (i === 0)
|
|
961
|
+
return col;
|
|
962
|
+
if (targetSet.has(col.name)) {
|
|
963
|
+
return { ...col, kind: 'number', required: false };
|
|
964
|
+
}
|
|
965
|
+
return col;
|
|
966
|
+
}));
|
|
967
|
+
const events = this.events;
|
|
968
|
+
if (events.length === 0) {
|
|
969
|
+
return _a.#fromTrustedEvents(this.name, outSchema, []);
|
|
970
|
+
}
|
|
971
|
+
const state = new Map();
|
|
972
|
+
for (const [name, reducer] of entries) {
|
|
973
|
+
if (typeof reducer === 'function') {
|
|
974
|
+
const fn = reducer;
|
|
975
|
+
state.set(name, {
|
|
976
|
+
acc: undefined,
|
|
977
|
+
apply: (acc, v) => (acc === undefined ? v : fn(acc, v)),
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
switch (reducer) {
|
|
982
|
+
case 'sum':
|
|
983
|
+
state.set(name, {
|
|
984
|
+
acc: undefined,
|
|
985
|
+
apply: (acc, v) => (acc ?? 0) + v,
|
|
986
|
+
});
|
|
987
|
+
break;
|
|
988
|
+
case 'count':
|
|
989
|
+
state.set(name, { acc: undefined, apply: (acc) => (acc ?? 0) + 1 });
|
|
990
|
+
break;
|
|
991
|
+
case 'max':
|
|
992
|
+
state.set(name, {
|
|
993
|
+
acc: undefined,
|
|
994
|
+
apply: (acc, v) => (acc === undefined || v > acc ? v : acc),
|
|
995
|
+
});
|
|
996
|
+
break;
|
|
997
|
+
case 'min':
|
|
998
|
+
state.set(name, {
|
|
999
|
+
acc: undefined,
|
|
1000
|
+
apply: (acc, v) => (acc === undefined || v < acc ? v : acc),
|
|
1001
|
+
});
|
|
1002
|
+
break;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
const resultEvents = [];
|
|
1007
|
+
for (const event of events) {
|
|
1008
|
+
const data = { ...event.data() };
|
|
1009
|
+
for (const [name, s] of state) {
|
|
1010
|
+
const raw = data[name];
|
|
1011
|
+
if (typeof raw === 'number') {
|
|
1012
|
+
s.acc = s.apply(s.acc, raw);
|
|
1013
|
+
data[name] = s.acc;
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
data[name] = s.acc;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
resultEvents.push(new Event(event.key(), data));
|
|
1020
|
+
}
|
|
1021
|
+
return _a.#fromTrustedEvents(this.name, outSchema, resultEvents);
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Example: `series.shift("value", 1)`.
|
|
1025
|
+
* Lags column values by N events (positive N) or leads them (negative N).
|
|
1026
|
+
* Vacated positions get `undefined`.
|
|
1027
|
+
*/
|
|
1028
|
+
shift(columns, n) {
|
|
1029
|
+
const cols = typeof columns === 'string' ? [columns] : columns;
|
|
1030
|
+
if (cols.length === 0) {
|
|
1031
|
+
throw new Error('shift() requires at least one column name');
|
|
1032
|
+
}
|
|
1033
|
+
if (!Number.isInteger(n)) {
|
|
1034
|
+
throw new Error('shift() requires an integer offset');
|
|
1035
|
+
}
|
|
1036
|
+
const targetSet = new Set(cols);
|
|
1037
|
+
const outSchema = Object.freeze(this.schema.map((col, i) => {
|
|
1038
|
+
if (i === 0)
|
|
1039
|
+
return col;
|
|
1040
|
+
if (targetSet.has(col.name)) {
|
|
1041
|
+
return { ...col, kind: 'number', required: false };
|
|
1042
|
+
}
|
|
1043
|
+
return col;
|
|
1044
|
+
}));
|
|
1045
|
+
const events = this.events;
|
|
1046
|
+
if (events.length === 0) {
|
|
1047
|
+
return _a.#fromTrustedEvents(this.name, outSchema, []);
|
|
1048
|
+
}
|
|
1049
|
+
const resultEvents = [];
|
|
1050
|
+
for (let i = 0; i < events.length; i++) {
|
|
1051
|
+
const data = { ...events[i].data() };
|
|
1052
|
+
const srcIdx = i - n;
|
|
1053
|
+
for (const col of cols) {
|
|
1054
|
+
if (srcIdx >= 0 && srcIdx < events.length) {
|
|
1055
|
+
data[col] = events[srcIdx].data()[col];
|
|
1056
|
+
}
|
|
1057
|
+
else {
|
|
1058
|
+
data[col] = undefined;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
resultEvents.push(new Event(events[i].key(), data));
|
|
1062
|
+
}
|
|
1063
|
+
return _a.#fromTrustedEvents(this.name, outSchema, resultEvents);
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Example: `series.fill("hold")`.
|
|
1067
|
+
* Fills `undefined` values using the given strategy for all payload columns.
|
|
944
1068
|
*
|
|
945
|
-
*
|
|
946
|
-
*
|
|
1069
|
+
* Example: `series.fill({ cpu: "linear", host: "hold" })`.
|
|
1070
|
+
* Per-column fill strategies. Unmentioned columns are left as-is.
|
|
1071
|
+
* Strategy names: `"hold"` (forward fill), `"linear"` (time-interpolated),
|
|
1072
|
+
* `"zero"` (fill with 0). A non-string value is used as a literal fill value.
|
|
947
1073
|
*
|
|
948
|
-
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
* throw new Error("empty series");
|
|
952
|
-
* }
|
|
1074
|
+
* Example: `series.fill("hold", { limit: 3 })`.
|
|
1075
|
+
* Caps consecutive fills per column. After `limit` consecutive fills, further
|
|
1076
|
+
* `undefined` values are left as-is until a real value resets the counter.
|
|
953
1077
|
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
* { value: "avg" },
|
|
957
|
-
* );
|
|
958
|
-
* ```
|
|
1078
|
+
* `"linear"` requires known values on both sides of a gap to interpolate.
|
|
1079
|
+
* Leading and trailing `undefined` runs are left unfilled.
|
|
959
1080
|
*/
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
{ name: 'interval', kind: 'interval' },
|
|
964
|
-
...this.schema
|
|
965
|
-
.slice(1)
|
|
966
|
-
.filter((column) => column.name in mapping)
|
|
967
|
-
.map((column) => {
|
|
968
|
-
const operation = mapping[column.name];
|
|
969
|
-
return {
|
|
970
|
-
name: column.name,
|
|
971
|
-
kind: operation === 'sum' ||
|
|
972
|
-
operation === 'avg' ||
|
|
973
|
-
operation === 'count'
|
|
974
|
-
? 'number'
|
|
975
|
-
: column.kind,
|
|
976
|
-
required: false,
|
|
977
|
-
};
|
|
978
|
-
}),
|
|
979
|
-
]);
|
|
980
|
-
if (!range) {
|
|
981
|
-
return new TimeSeries({
|
|
982
|
-
name: this.name,
|
|
983
|
-
schema: resultSchema,
|
|
984
|
-
rows: [],
|
|
985
|
-
});
|
|
1081
|
+
fill(strategy, options) {
|
|
1082
|
+
if (this.events.length === 0) {
|
|
1083
|
+
return this;
|
|
986
1084
|
}
|
|
987
|
-
const
|
|
988
|
-
const
|
|
989
|
-
if (
|
|
990
|
-
const
|
|
991
|
-
name:
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1085
|
+
const colNames = this.schema.slice(1).map((c) => c.name);
|
|
1086
|
+
const specs = new Map();
|
|
1087
|
+
if (typeof strategy === 'string') {
|
|
1088
|
+
for (const name of colNames) {
|
|
1089
|
+
specs.set(name, { mode: strategy });
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
else {
|
|
1093
|
+
const strategies = new Set([
|
|
1094
|
+
'hold',
|
|
1095
|
+
'bfill',
|
|
1096
|
+
'linear',
|
|
1097
|
+
'zero',
|
|
1098
|
+
]);
|
|
1099
|
+
for (const [name, spec] of Object.entries(strategy)) {
|
|
1100
|
+
if (typeof spec === 'string' && strategies.has(spec)) {
|
|
1101
|
+
specs.set(name, { mode: spec });
|
|
1000
1102
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1103
|
+
else {
|
|
1104
|
+
specs.set(name, { mode: 'literal', value: spec });
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
const limit = options?.limit;
|
|
1109
|
+
const n = this.events.length;
|
|
1110
|
+
const columns = {};
|
|
1111
|
+
for (const name of colNames) {
|
|
1112
|
+
columns[name] = new Array(n);
|
|
1113
|
+
}
|
|
1114
|
+
for (let i = 0; i < n; i++) {
|
|
1115
|
+
const data = this.events[i].data();
|
|
1116
|
+
for (const name of colNames) {
|
|
1117
|
+
columns[name][i] = data[name];
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
const times = new Array(n);
|
|
1121
|
+
for (let i = 0; i < n; i++) {
|
|
1122
|
+
times[i] = this.events[i].begin();
|
|
1123
|
+
}
|
|
1124
|
+
for (const [name, spec] of specs) {
|
|
1125
|
+
const col = columns[name];
|
|
1126
|
+
if (!col)
|
|
1127
|
+
continue;
|
|
1128
|
+
switch (spec.mode) {
|
|
1129
|
+
case 'hold': {
|
|
1130
|
+
let last;
|
|
1131
|
+
let consecutive = 0;
|
|
1132
|
+
for (let i = 0; i < n; i++) {
|
|
1133
|
+
if (col[i] !== undefined) {
|
|
1134
|
+
last = col[i];
|
|
1135
|
+
consecutive = 0;
|
|
1136
|
+
}
|
|
1137
|
+
else if (last !== undefined) {
|
|
1138
|
+
consecutive++;
|
|
1139
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1140
|
+
col[i] = last;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1008
1143
|
}
|
|
1009
|
-
|
|
1144
|
+
break;
|
|
1010
1145
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1146
|
+
case 'bfill': {
|
|
1147
|
+
let next;
|
|
1148
|
+
let consecutive = 0;
|
|
1149
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
1150
|
+
if (col[i] !== undefined) {
|
|
1151
|
+
next = col[i];
|
|
1152
|
+
consecutive = 0;
|
|
1153
|
+
}
|
|
1154
|
+
else if (next !== undefined) {
|
|
1155
|
+
consecutive++;
|
|
1156
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1157
|
+
col[i] = next;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
case 'zero': {
|
|
1164
|
+
let consecutive = 0;
|
|
1165
|
+
for (let i = 0; i < n; i++) {
|
|
1166
|
+
if (col[i] !== undefined) {
|
|
1167
|
+
consecutive = 0;
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
consecutive++;
|
|
1171
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1172
|
+
col[i] = 0;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1178
|
+
case 'literal': {
|
|
1179
|
+
let consecutive = 0;
|
|
1180
|
+
for (let i = 0; i < n; i++) {
|
|
1181
|
+
if (col[i] !== undefined) {
|
|
1182
|
+
consecutive = 0;
|
|
1183
|
+
}
|
|
1184
|
+
else {
|
|
1185
|
+
consecutive++;
|
|
1186
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1187
|
+
col[i] = spec.value;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
case 'linear': {
|
|
1194
|
+
let gapStart = -1;
|
|
1195
|
+
for (let i = 0; i < n; i++) {
|
|
1196
|
+
if (col[i] !== undefined) {
|
|
1197
|
+
if (gapStart >= 0 && gapStart > 0) {
|
|
1198
|
+
const before = col[gapStart - 1];
|
|
1199
|
+
const after = col[i];
|
|
1200
|
+
const t0 = times[gapStart - 1];
|
|
1201
|
+
const t1 = times[i];
|
|
1202
|
+
const span = t1 - t0;
|
|
1203
|
+
const gapLen = i - gapStart;
|
|
1204
|
+
for (let j = gapStart; j < i; j++) {
|
|
1205
|
+
const fillIndex = j - gapStart + 1;
|
|
1206
|
+
if (limit !== undefined && fillIndex > limit)
|
|
1207
|
+
break;
|
|
1208
|
+
if (span === 0) {
|
|
1209
|
+
col[j] = before;
|
|
1210
|
+
}
|
|
1211
|
+
else {
|
|
1212
|
+
const ratio = (times[j] - t0) / span;
|
|
1213
|
+
col[j] = before + (after - before) * ratio;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
gapStart = -1;
|
|
1218
|
+
}
|
|
1219
|
+
else if (gapStart < 0) {
|
|
1220
|
+
gapStart = i;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1022
1226
|
}
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
});
|
|
1033
|
-
return Object.freeze([bucket, ...aggregated]);
|
|
1034
|
-
});
|
|
1035
|
-
return new TimeSeries({
|
|
1036
|
-
name: this.name,
|
|
1037
|
-
schema: resultSchema,
|
|
1038
|
-
rows: resultRows,
|
|
1039
|
-
});
|
|
1227
|
+
const resultEvents = [];
|
|
1228
|
+
for (let i = 0; i < n; i++) {
|
|
1229
|
+
const data = {};
|
|
1230
|
+
for (const name of colNames) {
|
|
1231
|
+
data[name] = columns[name][i];
|
|
1232
|
+
}
|
|
1233
|
+
resultEvents.push(new Event(this.events[i].key(), data));
|
|
1234
|
+
}
|
|
1235
|
+
return _a.#fromTrustedEvents(this.name, this.schema, resultEvents);
|
|
1040
1236
|
}
|
|
1041
1237
|
rolling(sequenceOrWindow, windowOrMapping, mappingOrOptions, maybeOptions = {}) {
|
|
1042
1238
|
const buildResultColumns = () => this.schema
|
|
@@ -1072,7 +1268,7 @@ export class TimeSeries {
|
|
|
1072
1268
|
mappingOrOptions ??
|
|
1073
1269
|
{};
|
|
1074
1270
|
}
|
|
1075
|
-
const windowMs =
|
|
1271
|
+
const windowMs = parseDuration(window);
|
|
1076
1272
|
const alignment = options.alignment ?? 'trailing';
|
|
1077
1273
|
const anchorInWindow = (candidate, anchor) => {
|
|
1078
1274
|
if (alignment === 'trailing') {
|
|
@@ -1092,7 +1288,7 @@ export class TimeSeries {
|
|
|
1092
1288
|
...buildResultColumns(),
|
|
1093
1289
|
]);
|
|
1094
1290
|
if (!range) {
|
|
1095
|
-
return new
|
|
1291
|
+
return new _a({
|
|
1096
1292
|
name: this.name,
|
|
1097
1293
|
schema: resultSchema,
|
|
1098
1294
|
rows: [],
|
|
@@ -1103,16 +1299,16 @@ export class TimeSeries {
|
|
|
1103
1299
|
const anchor = sampleTime(bucket, sample);
|
|
1104
1300
|
const contributors = this.events.filter((candidate) => anchorInWindow(candidate.begin(), anchor));
|
|
1105
1301
|
const aggregated = resultSchema.slice(1).map((column) => {
|
|
1106
|
-
const
|
|
1302
|
+
const reducer = mapping[column.name];
|
|
1107
1303
|
const values = contributors.map((candidate) => {
|
|
1108
1304
|
const data = candidate.data();
|
|
1109
1305
|
return data[column.name];
|
|
1110
1306
|
});
|
|
1111
|
-
return
|
|
1307
|
+
return applyAggregateReducer(reducer, values);
|
|
1112
1308
|
});
|
|
1113
1309
|
return Object.freeze([bucket, ...aggregated]);
|
|
1114
1310
|
});
|
|
1115
|
-
return new
|
|
1311
|
+
return new _a({
|
|
1116
1312
|
name: this.name,
|
|
1117
1313
|
schema: resultSchema,
|
|
1118
1314
|
rows: resultRows,
|
|
@@ -1123,7 +1319,12 @@ export class TimeSeries {
|
|
|
1123
1319
|
this.schema[0],
|
|
1124
1320
|
...resultColumns,
|
|
1125
1321
|
]);
|
|
1126
|
-
const reducerStates = resultColumns.map((column) =>
|
|
1322
|
+
const reducerStates = resultColumns.map((column) => {
|
|
1323
|
+
const reducer = mapping[column.name];
|
|
1324
|
+
return isBuiltInAggregateReducer(reducer)
|
|
1325
|
+
? createRollingReducerState(reducer)
|
|
1326
|
+
: null;
|
|
1327
|
+
});
|
|
1127
1328
|
const beginTimes = this.events.map((event) => event.begin());
|
|
1128
1329
|
const resultRows = new Array(this.events.length);
|
|
1129
1330
|
let windowStart = 0;
|
|
@@ -1131,19 +1332,38 @@ export class TimeSeries {
|
|
|
1131
1332
|
const addEvent = (index) => {
|
|
1132
1333
|
const event = this.events[index];
|
|
1133
1334
|
const data = event.data();
|
|
1134
|
-
for (let
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1335
|
+
for (let i = 0; i < reducerStates.length; i++) {
|
|
1336
|
+
const state = reducerStates[i];
|
|
1337
|
+
if (state) {
|
|
1338
|
+
const column = resultColumns[i];
|
|
1339
|
+
state.add(index, data[column.name]);
|
|
1340
|
+
}
|
|
1137
1341
|
}
|
|
1138
1342
|
};
|
|
1139
1343
|
const removeEvent = (index) => {
|
|
1140
1344
|
const event = this.events[index];
|
|
1141
1345
|
const data = event.data();
|
|
1142
|
-
for (let
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1346
|
+
for (let i = 0; i < reducerStates.length; i++) {
|
|
1347
|
+
const state = reducerStates[i];
|
|
1348
|
+
if (state) {
|
|
1349
|
+
const column = resultColumns[i];
|
|
1350
|
+
state.remove(index, data[column.name]);
|
|
1351
|
+
}
|
|
1145
1352
|
}
|
|
1146
1353
|
};
|
|
1354
|
+
const snapshotWindow = () => resultColumns.map((column, i) => {
|
|
1355
|
+
const state = reducerStates[i];
|
|
1356
|
+
if (state)
|
|
1357
|
+
return state.snapshot();
|
|
1358
|
+
const reducer = mapping[column.name];
|
|
1359
|
+
const values = this.events
|
|
1360
|
+
.slice(windowStart, windowEnd)
|
|
1361
|
+
.map((event) => {
|
|
1362
|
+
const data = event.data();
|
|
1363
|
+
return data[column.name];
|
|
1364
|
+
});
|
|
1365
|
+
return applyAggregateReducer(reducer, values);
|
|
1366
|
+
});
|
|
1147
1367
|
if (alignment === 'trailing') {
|
|
1148
1368
|
for (let groupStart = 0; groupStart < this.events.length;) {
|
|
1149
1369
|
const anchor = beginTimes[groupStart];
|
|
@@ -1163,7 +1383,7 @@ export class TimeSeries {
|
|
|
1163
1383
|
removeEvent(windowStart);
|
|
1164
1384
|
windowStart += 1;
|
|
1165
1385
|
}
|
|
1166
|
-
const aggregated =
|
|
1386
|
+
const aggregated = snapshotWindow();
|
|
1167
1387
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
1168
1388
|
resultRows[index] = Object.freeze([
|
|
1169
1389
|
this.events[index].key(),
|
|
@@ -1193,7 +1413,7 @@ export class TimeSeries {
|
|
|
1193
1413
|
addEvent(windowEnd);
|
|
1194
1414
|
windowEnd += 1;
|
|
1195
1415
|
}
|
|
1196
|
-
const aggregated =
|
|
1416
|
+
const aggregated = snapshotWindow();
|
|
1197
1417
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
1198
1418
|
resultRows[index] = Object.freeze([
|
|
1199
1419
|
this.events[index].key(),
|
|
@@ -1224,7 +1444,7 @@ export class TimeSeries {
|
|
|
1224
1444
|
addEvent(windowEnd);
|
|
1225
1445
|
windowEnd += 1;
|
|
1226
1446
|
}
|
|
1227
|
-
const aggregated =
|
|
1447
|
+
const aggregated = snapshotWindow();
|
|
1228
1448
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
1229
1449
|
resultRows[index] = Object.freeze([
|
|
1230
1450
|
this.events[index].key(),
|
|
@@ -1234,7 +1454,7 @@ export class TimeSeries {
|
|
|
1234
1454
|
groupStart = groupEnd;
|
|
1235
1455
|
}
|
|
1236
1456
|
}
|
|
1237
|
-
return new
|
|
1457
|
+
return new _a({
|
|
1238
1458
|
name: this.name,
|
|
1239
1459
|
schema: resultSchema,
|
|
1240
1460
|
rows: resultRows,
|
|
@@ -1299,7 +1519,7 @@ export class TimeSeries {
|
|
|
1299
1519
|
.map((nextColumn) => nextEvent.data()[nextColumn.name]),
|
|
1300
1520
|
]);
|
|
1301
1521
|
});
|
|
1302
|
-
return new
|
|
1522
|
+
return new _a({
|
|
1303
1523
|
name: this.name,
|
|
1304
1524
|
schema: resultSchema,
|
|
1305
1525
|
rows: resultRows,
|
|
@@ -1337,7 +1557,7 @@ export class TimeSeries {
|
|
|
1337
1557
|
.map((nextColumn) => nextEvent.data()[nextColumn.name]),
|
|
1338
1558
|
]);
|
|
1339
1559
|
});
|
|
1340
|
-
return new
|
|
1560
|
+
return new _a({
|
|
1341
1561
|
name: this.name,
|
|
1342
1562
|
schema: resultSchema,
|
|
1343
1563
|
rows: resultRows,
|
|
@@ -1347,7 +1567,7 @@ export class TimeSeries {
|
|
|
1347
1567
|
throw new TypeError('movingAverage smoothing requires a window option');
|
|
1348
1568
|
}
|
|
1349
1569
|
const window = options.window;
|
|
1350
|
-
const windowMs =
|
|
1570
|
+
const windowMs = parseDuration(window);
|
|
1351
1571
|
const alignment = options.alignment ?? 'trailing';
|
|
1352
1572
|
const resultValues = new Array(this.events.length);
|
|
1353
1573
|
let windowStart = 0;
|
|
@@ -1431,7 +1651,7 @@ export class TimeSeries {
|
|
|
1431
1651
|
.map((nextColumn) => nextEvent.data()[nextColumn.name]),
|
|
1432
1652
|
]);
|
|
1433
1653
|
});
|
|
1434
|
-
return new
|
|
1654
|
+
return new _a({
|
|
1435
1655
|
name: this.name,
|
|
1436
1656
|
schema: resultSchema,
|
|
1437
1657
|
rows: resultRows,
|
|
@@ -1439,11 +1659,11 @@ export class TimeSeries {
|
|
|
1439
1659
|
}
|
|
1440
1660
|
/** Example: `series.slice(0, 10)`. Returns a positional half-open slice of the series. */
|
|
1441
1661
|
slice(beginIndex, endIndex) {
|
|
1442
|
-
return
|
|
1662
|
+
return _a.#fromTrustedEvents(this.name, this.schema, this.events.slice(beginIndex, endIndex));
|
|
1443
1663
|
}
|
|
1444
1664
|
/** Example: `series.filter(event => event.get("active"))`. Returns a new series containing only events that match the predicate. */
|
|
1445
1665
|
filter(predicate) {
|
|
1446
|
-
return
|
|
1666
|
+
return _a.#fromTrustedEvents(this.name, this.schema, this.events.filter((event, index) => predicate(event, index)));
|
|
1447
1667
|
}
|
|
1448
1668
|
/** Example: `series.find(event => event.get("value") > 0)`. Returns the first event that matches the predicate, if any. */
|
|
1449
1669
|
find(predicate) {
|
|
@@ -1564,7 +1784,7 @@ export class TimeSeries {
|
|
|
1564
1784
|
const trimmedEvents = this.events
|
|
1565
1785
|
.map((event) => event.trim(range))
|
|
1566
1786
|
.filter((event) => event !== undefined);
|
|
1567
|
-
return
|
|
1787
|
+
return _a.#fromTrustedEvents(this.name, this.schema, trimmedEvents);
|
|
1568
1788
|
}
|
|
1569
1789
|
/** Example: `series.before(Date.now())`. Returns the events ending strictly before the supplied temporal boundary. */
|
|
1570
1790
|
before(boundary) {
|
|
@@ -1596,7 +1816,7 @@ export class TimeSeries {
|
|
|
1596
1816
|
const selectedEvent = event.select(...keys);
|
|
1597
1817
|
return selectedEvent;
|
|
1598
1818
|
});
|
|
1599
|
-
return
|
|
1819
|
+
return _a.#fromTrustedEvents(this.name, resultSchema, resultEvents);
|
|
1600
1820
|
}
|
|
1601
1821
|
/** Example: `series.rename({ cpu: "usage" })`. Returns a new series with payload field names renamed according to the supplied mapping. */
|
|
1602
1822
|
rename(mapping) {
|
|
@@ -1614,7 +1834,7 @@ export class TimeSeries {
|
|
|
1614
1834
|
const renamedEvent = event.rename(mapping);
|
|
1615
1835
|
return renamedEvent;
|
|
1616
1836
|
});
|
|
1617
|
-
return
|
|
1837
|
+
return _a.#fromTrustedEvents(this.name, resultSchema, resultEvents);
|
|
1618
1838
|
}
|
|
1619
1839
|
collapse(keys, output, reducer, options) {
|
|
1620
1840
|
const nextEvents = this.events.map((event) => {
|
|
@@ -1642,54 +1862,138 @@ export class TimeSeries {
|
|
|
1642
1862
|
: 'string',
|
|
1643
1863
|
},
|
|
1644
1864
|
]);
|
|
1645
|
-
return
|
|
1865
|
+
return _a.#fromTrustedEvents(this.name, resultSchema, nextEvents);
|
|
1646
1866
|
}
|
|
1647
1867
|
/** Example: `series.length`. Returns the number of events in the series. */
|
|
1648
1868
|
get length() {
|
|
1649
1869
|
return this.events.length;
|
|
1650
1870
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1871
|
+
}
|
|
1872
|
+
_a = TimeSeries;
|
|
1873
|
+
function aggregateInternal(series, sequence, mapping, options = {}) {
|
|
1874
|
+
const range = options.range ?? series.timeRange();
|
|
1875
|
+
const aggregateColumns = normalizeAggregateColumns(series.schema, mapping);
|
|
1876
|
+
const resultSchema = Object.freeze([
|
|
1877
|
+
{ name: 'interval', kind: 'interval' },
|
|
1878
|
+
...aggregateColumns.map((column) => ({
|
|
1879
|
+
name: column.output,
|
|
1880
|
+
kind: column.kind,
|
|
1881
|
+
required: false,
|
|
1882
|
+
})),
|
|
1883
|
+
]);
|
|
1884
|
+
if (!range) {
|
|
1885
|
+
return new TimeSeries({
|
|
1886
|
+
name: series.name,
|
|
1887
|
+
schema: resultSchema,
|
|
1888
|
+
rows: [],
|
|
1889
|
+
});
|
|
1654
1890
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1891
|
+
const buckets = toBoundedSequence(sequence, range, 'begin').intervals();
|
|
1892
|
+
const columns = aggregateColumns;
|
|
1893
|
+
if (isTimeKeyed(series)) {
|
|
1894
|
+
const builtInOnly = columns.every((column) => isBuiltInAggregateReducer(column.reducer));
|
|
1895
|
+
let eventIndex = 0;
|
|
1896
|
+
const resultRows = buckets.map((bucket) => {
|
|
1897
|
+
const states = builtInOnly
|
|
1898
|
+
? columns.map((column) => createAggregateBucketState(column.reducer))
|
|
1899
|
+
: undefined;
|
|
1900
|
+
while (eventIndex < series.events.length &&
|
|
1901
|
+
series.events[eventIndex].begin() < bucket.begin()) {
|
|
1902
|
+
eventIndex += 1;
|
|
1662
1903
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
const nextValue = nextData[column.name];
|
|
1683
|
-
if (column.kind === 'number' &&
|
|
1684
|
-
typeof previousValue === 'number' &&
|
|
1685
|
-
typeof nextValue === 'number') {
|
|
1686
|
-
result[column.name] =
|
|
1687
|
-
previousValue + (nextValue - previousValue) * ratio;
|
|
1688
|
-
continue;
|
|
1904
|
+
const bucketStart = eventIndex;
|
|
1905
|
+
let scanIndex = bucketStart;
|
|
1906
|
+
while (scanIndex < series.events.length &&
|
|
1907
|
+
series.events[scanIndex].begin() < bucket.end()) {
|
|
1908
|
+
if (states) {
|
|
1909
|
+
const data = series.events[scanIndex].data();
|
|
1910
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1911
|
+
const column = columns[index];
|
|
1912
|
+
states[index].add(data[column.source]);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
scanIndex += 1;
|
|
1916
|
+
}
|
|
1917
|
+
eventIndex = scanIndex;
|
|
1918
|
+
if (states) {
|
|
1919
|
+
return Object.freeze([
|
|
1920
|
+
bucket,
|
|
1921
|
+
...states.map((state) => state.snapshot()),
|
|
1922
|
+
]);
|
|
1689
1923
|
}
|
|
1690
|
-
|
|
1924
|
+
const contributors = series.events.slice(bucketStart, scanIndex);
|
|
1925
|
+
const aggregated = columns.map((column) => {
|
|
1926
|
+
const values = contributors.map((event) => {
|
|
1927
|
+
const data = event.data();
|
|
1928
|
+
return data[column.source];
|
|
1929
|
+
});
|
|
1930
|
+
return applyAggregateReducer(column.reducer, values);
|
|
1931
|
+
});
|
|
1932
|
+
return Object.freeze([bucket, ...aggregated]);
|
|
1933
|
+
});
|
|
1934
|
+
return new TimeSeries({
|
|
1935
|
+
name: series.name,
|
|
1936
|
+
schema: resultSchema,
|
|
1937
|
+
rows: resultRows,
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
const resultRows = buckets.map((bucket) => {
|
|
1941
|
+
const contributors = series.events.filter((event) => bucketOverlapsHalfOpen(bucket, event.key()));
|
|
1942
|
+
const aggregated = columns.map((column) => {
|
|
1943
|
+
const values = contributors.map((event) => {
|
|
1944
|
+
const data = event.data();
|
|
1945
|
+
return data[column.source];
|
|
1946
|
+
});
|
|
1947
|
+
return applyAggregateReducer(column.reducer, values);
|
|
1948
|
+
});
|
|
1949
|
+
return Object.freeze([bucket, ...aggregated]);
|
|
1950
|
+
});
|
|
1951
|
+
return new TimeSeries({
|
|
1952
|
+
name: series.name,
|
|
1953
|
+
schema: resultSchema,
|
|
1954
|
+
rows: resultRows,
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
function alignHoldAt(series, t) {
|
|
1958
|
+
const event = series.atOrBefore(new Time(t));
|
|
1959
|
+
return (event?.data() ?? {});
|
|
1960
|
+
}
|
|
1961
|
+
function alignLinearAt(series, t, valueColumns, cursor) {
|
|
1962
|
+
const events = series.events;
|
|
1963
|
+
const hasCursor = cursor !== undefined;
|
|
1964
|
+
let index = hasCursor ? cursor.index : series.bisect(t);
|
|
1965
|
+
if (hasCursor) {
|
|
1966
|
+
while (index < events.length && events[index].begin() < t) {
|
|
1967
|
+
index += 1;
|
|
1691
1968
|
}
|
|
1692
|
-
|
|
1969
|
+
cursor.index = index;
|
|
1970
|
+
}
|
|
1971
|
+
if (index < events.length && events[index].begin() === t) {
|
|
1972
|
+
return events[index].data();
|
|
1973
|
+
}
|
|
1974
|
+
if (index === 0) {
|
|
1975
|
+
return {};
|
|
1976
|
+
}
|
|
1977
|
+
const previous = events[index - 1];
|
|
1978
|
+
const next = events[index];
|
|
1979
|
+
if (!next || previous.begin() === next.begin()) {
|
|
1980
|
+
return previous.data();
|
|
1981
|
+
}
|
|
1982
|
+
const ratio = (t - previous.begin()) / (next.begin() - previous.begin());
|
|
1983
|
+
const result = {};
|
|
1984
|
+
const previousData = previous.data();
|
|
1985
|
+
const nextData = next.data();
|
|
1986
|
+
for (const column of valueColumns) {
|
|
1987
|
+
const previousValue = previousData[column.name];
|
|
1988
|
+
const nextValue = nextData[column.name];
|
|
1989
|
+
if (column.kind === 'number' &&
|
|
1990
|
+
typeof previousValue === 'number' &&
|
|
1991
|
+
typeof nextValue === 'number') {
|
|
1992
|
+
result[column.name] = previousValue + (nextValue - previousValue) * ratio;
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
result[column.name] = previousValue;
|
|
1693
1996
|
}
|
|
1997
|
+
return result;
|
|
1694
1998
|
}
|
|
1695
1999
|
//# sourceMappingURL=TimeSeries.js.map
|