pond-ts 0.2.0 → 0.3.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/README.md +14 -0
- package/dist/TimeSeries.d.ts +132 -2
- package/dist/TimeSeries.d.ts.map +1 -1
- package/dist/TimeSeries.js +745 -418
- package/dist/TimeSeries.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.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 +28 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/TimeSeries.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
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 { resolveReducer, } from './reducers/index.js';
|
|
8
11
|
function isObjectRow(value) {
|
|
9
12
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
10
13
|
}
|
|
@@ -89,6 +92,23 @@ function parseJsonRows(schema, rows, options = {}) {
|
|
|
89
92
|
}));
|
|
90
93
|
});
|
|
91
94
|
}
|
|
95
|
+
function serializeJsonKey(kind, key, rowFormat) {
|
|
96
|
+
if (kind === 'time') {
|
|
97
|
+
return key.begin();
|
|
98
|
+
}
|
|
99
|
+
if (kind === 'timeRange') {
|
|
100
|
+
return rowFormat === 'object'
|
|
101
|
+
? { start: key.begin(), end: key.end() }
|
|
102
|
+
: [key.begin(), key.end()];
|
|
103
|
+
}
|
|
104
|
+
const interval = key;
|
|
105
|
+
return rowFormat === 'object'
|
|
106
|
+
? { value: interval.value, start: interval.begin(), end: interval.end() }
|
|
107
|
+
: [interval.value, interval.begin(), interval.end()];
|
|
108
|
+
}
|
|
109
|
+
function serializeJsonValue(value) {
|
|
110
|
+
return value === undefined ? null : value;
|
|
111
|
+
}
|
|
92
112
|
function toRows(schema, events) {
|
|
93
113
|
return events.map((event) => {
|
|
94
114
|
const data = event.data();
|
|
@@ -100,6 +120,20 @@ function toRows(schema, events) {
|
|
|
100
120
|
]);
|
|
101
121
|
});
|
|
102
122
|
}
|
|
123
|
+
function toObjects(schema, events) {
|
|
124
|
+
const keyColumn = schema[0];
|
|
125
|
+
const dataColumns = schema.slice(1);
|
|
126
|
+
return events.map((event) => {
|
|
127
|
+
const row = {
|
|
128
|
+
[keyColumn.name]: event.key(),
|
|
129
|
+
};
|
|
130
|
+
const data = event.data();
|
|
131
|
+
for (const column of dataColumns) {
|
|
132
|
+
row[column.name] = data[column.name];
|
|
133
|
+
}
|
|
134
|
+
return Object.freeze(row);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
103
137
|
function isEventKey(value) {
|
|
104
138
|
return (typeof value === 'object' &&
|
|
105
139
|
value !== null &&
|
|
@@ -316,254 +350,60 @@ function bucketOverlapsHalfOpen(bucket, event) {
|
|
|
316
350
|
function aggregateValues(operation, values) {
|
|
317
351
|
const defined = values.filter((value) => value !== undefined);
|
|
318
352
|
const numeric = defined.filter((value) => typeof value === 'number');
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
353
|
+
return resolveReducer(operation).reduce(defined, numeric);
|
|
354
|
+
}
|
|
355
|
+
function isBuiltInAggregateReducer(reducer) {
|
|
356
|
+
return typeof reducer === 'string';
|
|
357
|
+
}
|
|
358
|
+
function applyAggregateReducer(reducer, values) {
|
|
359
|
+
return isBuiltInAggregateReducer(reducer)
|
|
360
|
+
? aggregateValues(reducer, values)
|
|
361
|
+
: reducer(values);
|
|
362
|
+
}
|
|
363
|
+
function isAggregateOutputSpec(value) {
|
|
364
|
+
return (typeof value === 'object' &&
|
|
365
|
+
value !== null &&
|
|
366
|
+
'from' in value &&
|
|
367
|
+
'using' in value);
|
|
368
|
+
}
|
|
369
|
+
function normalizeAggregateColumns(schema, mapping) {
|
|
370
|
+
const columnsByName = new Map(schema.slice(1).map((column) => [column.name, column]));
|
|
371
|
+
const normalized = [];
|
|
372
|
+
for (const [outputName, raw] of Object.entries(mapping)) {
|
|
373
|
+
const sourceName = isAggregateOutputSpec(raw) ? raw.from : outputName;
|
|
374
|
+
const sourceColumn = columnsByName.get(sourceName);
|
|
375
|
+
if (!sourceColumn) {
|
|
376
|
+
throw new TypeError(`aggregate mapping references unknown source column '${sourceName}'`);
|
|
377
|
+
}
|
|
378
|
+
if (sourceColumn.kind !== 'number' &&
|
|
379
|
+
sourceColumn.kind !== 'string' &&
|
|
380
|
+
sourceColumn.kind !== 'boolean') {
|
|
381
|
+
throw new TypeError(`aggregate source column '${sourceName}' must be a scalar value column`);
|
|
382
|
+
}
|
|
383
|
+
const reducer = isAggregateOutputSpec(raw) ? raw.using : raw;
|
|
384
|
+
if (typeof reducer !== 'string' && typeof reducer !== 'function') {
|
|
385
|
+
throw new TypeError(`aggregate reducer for '${outputName}' must be a built-in name or function`);
|
|
386
|
+
}
|
|
387
|
+
const explicitKind = isAggregateOutputSpec(raw) ? raw.kind : undefined;
|
|
388
|
+
const resolvedKind = explicitKind ??
|
|
389
|
+
(typeof reducer === 'string' &&
|
|
390
|
+
resolveReducer(reducer).outputKind === 'number'
|
|
391
|
+
? 'number'
|
|
392
|
+
: sourceColumn.kind);
|
|
393
|
+
normalized.push({
|
|
394
|
+
output: outputName,
|
|
395
|
+
source: sourceName,
|
|
396
|
+
reducer,
|
|
397
|
+
kind: resolvedKind,
|
|
398
|
+
});
|
|
340
399
|
}
|
|
400
|
+
return normalized;
|
|
341
401
|
}
|
|
342
402
|
function createAggregateBucketState(operation) {
|
|
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}`);
|
|
403
|
+
return resolveReducer(operation).bucketState();
|
|
445
404
|
}
|
|
446
405
|
function createRollingReducerState(operation) {
|
|
447
|
-
|
|
448
|
-
if (head > 0 && head * 2 >= entries.length) {
|
|
449
|
-
return [entries.slice(head), 0];
|
|
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}`);
|
|
406
|
+
return resolveReducer(operation).rollingState();
|
|
567
407
|
}
|
|
568
408
|
function parseDurationInput(value) {
|
|
569
409
|
if (typeof value === 'number') {
|
|
@@ -697,7 +537,7 @@ export class TimeSeries {
|
|
|
697
537
|
* the supplied `parse.timeZone`, which defaults to `UTC`.
|
|
698
538
|
*/
|
|
699
539
|
static fromJSON(input) {
|
|
700
|
-
return new
|
|
540
|
+
return new _a({
|
|
701
541
|
name: input.name,
|
|
702
542
|
schema: input.schema,
|
|
703
543
|
rows: parseJsonRows(input.schema, input.rows, input.parse),
|
|
@@ -710,6 +550,48 @@ export class TimeSeries {
|
|
|
710
550
|
this.events = validateAndNormalize(input);
|
|
711
551
|
Object.freeze(this);
|
|
712
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Example: `series.toJSON({ rowFormat: "object" })`.
|
|
555
|
+
* Serializes the series into the JSON-friendly shape accepted by `TimeSeries.fromJSON(...)`.
|
|
556
|
+
*
|
|
557
|
+
* Timestamps are emitted as numbers to avoid time zone ambiguity. Missing payload values are
|
|
558
|
+
* emitted as `null`. By default rows are emitted as arrays; use `rowFormat: "object"` for rows
|
|
559
|
+
* keyed by schema column names.
|
|
560
|
+
*/
|
|
561
|
+
toJSON(options = {}) {
|
|
562
|
+
const rowFormat = options.rowFormat ?? 'array';
|
|
563
|
+
const dataColumns = this.schema.slice(1);
|
|
564
|
+
if (rowFormat === 'object') {
|
|
565
|
+
const keyColumn = this.schema[0];
|
|
566
|
+
const rows = this.events.map((event) => {
|
|
567
|
+
const row = {
|
|
568
|
+
[keyColumn.name]: serializeJsonKey(keyColumn.kind, event.key(), rowFormat),
|
|
569
|
+
};
|
|
570
|
+
const data = event.data();
|
|
571
|
+
for (const column of dataColumns) {
|
|
572
|
+
row[column.name] = serializeJsonValue(data[column.name]);
|
|
573
|
+
}
|
|
574
|
+
return Object.freeze(row);
|
|
575
|
+
});
|
|
576
|
+
return {
|
|
577
|
+
name: this.name,
|
|
578
|
+
schema: this.schema,
|
|
579
|
+
rows,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
const rows = this.events.map((event) => {
|
|
583
|
+
const data = event.data();
|
|
584
|
+
return Object.freeze([
|
|
585
|
+
serializeJsonKey(this.schema[0].kind, event.key(), rowFormat),
|
|
586
|
+
...dataColumns.map((column) => serializeJsonValue(data[column.name])),
|
|
587
|
+
]);
|
|
588
|
+
});
|
|
589
|
+
return {
|
|
590
|
+
name: this.name,
|
|
591
|
+
schema: this.schema,
|
|
592
|
+
rows,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
713
595
|
/**
|
|
714
596
|
* Builds a series from event data that has already been validated and ordered by the caller.
|
|
715
597
|
*
|
|
@@ -717,7 +599,7 @@ export class TimeSeries {
|
|
|
717
599
|
* order and normalized key invariants.
|
|
718
600
|
*/
|
|
719
601
|
static #fromTrustedEvents(name, schema, events) {
|
|
720
|
-
const series = Object.create(
|
|
602
|
+
const series = Object.create(_a.prototype);
|
|
721
603
|
series.name = name;
|
|
722
604
|
series.schema = Object.freeze(schema.slice());
|
|
723
605
|
series.events = Object.freeze(events.slice());
|
|
@@ -731,6 +613,14 @@ export class TimeSeries {
|
|
|
731
613
|
get rows() {
|
|
732
614
|
return toRows(this.schema, this.events);
|
|
733
615
|
}
|
|
616
|
+
/** Example: `series.toRows()`. Returns normalized row arrays using `Time`/`TimeRange`/`Interval` keys and `undefined` for missing payload values. */
|
|
617
|
+
toRows() {
|
|
618
|
+
return this.rows;
|
|
619
|
+
}
|
|
620
|
+
/** Example: `series.toObjects()`. Returns normalized schema-keyed object rows using temporal key objects and `undefined` for missing payload values. */
|
|
621
|
+
toObjects() {
|
|
622
|
+
return toObjects(this.schema, this.events);
|
|
623
|
+
}
|
|
734
624
|
/** Example: `series.at(0)`. Returns the event at the supplied zero-based position, if present. */
|
|
735
625
|
at(index) {
|
|
736
626
|
return this.events[index];
|
|
@@ -748,7 +638,7 @@ export class TimeSeries {
|
|
|
748
638
|
/** Example: `series.map(nextSchema, event => event)`. Maps each event into a new typed schema and returns a new series. */
|
|
749
639
|
map(schema, mapper) {
|
|
750
640
|
const mappedEvents = this.events.map((event, index) => mapper(event, index));
|
|
751
|
-
return new
|
|
641
|
+
return new _a({
|
|
752
642
|
name: this.name,
|
|
753
643
|
schema,
|
|
754
644
|
rows: toRows(schema, mappedEvents),
|
|
@@ -762,9 +652,9 @@ export class TimeSeries {
|
|
|
762
652
|
]);
|
|
763
653
|
const resultEvents = this.events.map((event) => event.asTime(options));
|
|
764
654
|
if ((options.at ?? 'begin') === 'begin') {
|
|
765
|
-
return
|
|
655
|
+
return _a.#fromTrustedEvents(this.name, schema, resultEvents);
|
|
766
656
|
}
|
|
767
|
-
return new
|
|
657
|
+
return new _a({
|
|
768
658
|
name: this.name,
|
|
769
659
|
schema,
|
|
770
660
|
rows: toRows(schema, resultEvents),
|
|
@@ -777,7 +667,7 @@ export class TimeSeries {
|
|
|
777
667
|
...this.schema.slice(1),
|
|
778
668
|
]);
|
|
779
669
|
const resultEvents = this.events.map((event) => event.asTimeRange());
|
|
780
|
-
return
|
|
670
|
+
return _a.#fromTrustedEvents(this.name, schema, resultEvents);
|
|
781
671
|
}
|
|
782
672
|
asInterval(value) {
|
|
783
673
|
const schema = Object.freeze([
|
|
@@ -789,10 +679,13 @@ export class TimeSeries {
|
|
|
789
679
|
? event.asInterval(() => value(event, index))
|
|
790
680
|
: event.asInterval(value);
|
|
791
681
|
});
|
|
792
|
-
return
|
|
682
|
+
return _a.#fromTrustedEvents(this.name, schema, nextEvents);
|
|
793
683
|
}
|
|
794
684
|
join(other, options = {}) {
|
|
795
|
-
const [left, right] = prepareSeriesForJoin([
|
|
685
|
+
const [left, right] = prepareSeriesForJoin([
|
|
686
|
+
this,
|
|
687
|
+
other,
|
|
688
|
+
], options);
|
|
796
689
|
const joinType = options.type ?? 'outer';
|
|
797
690
|
if (left.firstColumnKind !== right.firstColumnKind) {
|
|
798
691
|
throw new TypeError('cannot join series with different key kinds');
|
|
@@ -845,7 +738,7 @@ export class TimeSeries {
|
|
|
845
738
|
rightIndex += 1;
|
|
846
739
|
}
|
|
847
740
|
}
|
|
848
|
-
return
|
|
741
|
+
return _a.#fromTrustedEvents(left.name, resultSchema, joinedEvents);
|
|
849
742
|
}
|
|
850
743
|
/**
|
|
851
744
|
* Example: `series.align(Sequence.every("1m"))`.
|
|
@@ -877,7 +770,7 @@ export class TimeSeries {
|
|
|
877
770
|
const range = options.range ?? this.timeRange();
|
|
878
771
|
const resultSchema = makeAlignedSchema(this.schema);
|
|
879
772
|
if (!range) {
|
|
880
|
-
return new
|
|
773
|
+
return new _a({
|
|
881
774
|
name: this.name,
|
|
882
775
|
schema: resultSchema,
|
|
883
776
|
rows: [],
|
|
@@ -896,7 +789,7 @@ export class TimeSeries {
|
|
|
896
789
|
for (let i = 0; i < intervals.length; i += 1) {
|
|
897
790
|
const interval = intervals[i];
|
|
898
791
|
const t = sampleTime(interval, sample);
|
|
899
|
-
const data =
|
|
792
|
+
const data = alignLinearAt(this, t, valueColumns, cursor);
|
|
900
793
|
const row = new Array(resultColumns.length + 1);
|
|
901
794
|
row[0] = interval;
|
|
902
795
|
for (let j = 0; j < resultColumns.length; j += 1) {
|
|
@@ -909,7 +802,7 @@ export class TimeSeries {
|
|
|
909
802
|
})()
|
|
910
803
|
: intervals.map((interval) => {
|
|
911
804
|
const t = sampleTime(interval, sample);
|
|
912
|
-
const data =
|
|
805
|
+
const data = alignHoldAt(this, t);
|
|
913
806
|
return Object.freeze([
|
|
914
807
|
interval,
|
|
915
808
|
...resultSchema
|
|
@@ -917,126 +810,452 @@ export class TimeSeries {
|
|
|
917
810
|
.map((column) => data[column.name]),
|
|
918
811
|
]);
|
|
919
812
|
});
|
|
920
|
-
return new
|
|
813
|
+
return new _a({
|
|
921
814
|
name: this.name,
|
|
922
815
|
schema: resultSchema,
|
|
923
816
|
rows: alignedRows,
|
|
924
817
|
});
|
|
925
818
|
}
|
|
819
|
+
aggregate(sequence, mapping, options = {}) {
|
|
820
|
+
return aggregateInternal(this, sequence, mapping, options);
|
|
821
|
+
}
|
|
822
|
+
reduce(columnOrMapping, reducer) {
|
|
823
|
+
if (typeof columnOrMapping === 'string') {
|
|
824
|
+
const values = this.events.map((event) => {
|
|
825
|
+
const data = event.data();
|
|
826
|
+
return data[columnOrMapping];
|
|
827
|
+
});
|
|
828
|
+
return applyAggregateReducer(reducer, values);
|
|
829
|
+
}
|
|
830
|
+
const columns = normalizeAggregateColumns(this.schema, columnOrMapping);
|
|
831
|
+
const result = {};
|
|
832
|
+
for (const col of columns) {
|
|
833
|
+
const values = this.events.map((event) => {
|
|
834
|
+
const data = event.data();
|
|
835
|
+
return data[col.source];
|
|
836
|
+
});
|
|
837
|
+
result[col.output] = applyAggregateReducer(col.reducer, values);
|
|
838
|
+
}
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
groupBy(column, transform) {
|
|
842
|
+
const buckets = new Map();
|
|
843
|
+
for (const event of this.events) {
|
|
844
|
+
const raw = event.data()[column];
|
|
845
|
+
const key = raw === undefined ? 'undefined' : String(raw);
|
|
846
|
+
let bucket = buckets.get(key);
|
|
847
|
+
if (!bucket) {
|
|
848
|
+
bucket = [];
|
|
849
|
+
buckets.set(key, bucket);
|
|
850
|
+
}
|
|
851
|
+
bucket.push(event);
|
|
852
|
+
}
|
|
853
|
+
const buildGroup = (events) => new _a({
|
|
854
|
+
name: this.name,
|
|
855
|
+
schema: this.schema,
|
|
856
|
+
rows: toRows(this.schema, events),
|
|
857
|
+
});
|
|
858
|
+
if (transform) {
|
|
859
|
+
const result = new Map();
|
|
860
|
+
for (const [key, events] of buckets) {
|
|
861
|
+
result.set(key, transform(buildGroup(events), key));
|
|
862
|
+
}
|
|
863
|
+
return result;
|
|
864
|
+
}
|
|
865
|
+
const result = new Map();
|
|
866
|
+
for (const [key, events] of buckets) {
|
|
867
|
+
result.set(key, buildGroup(events));
|
|
868
|
+
}
|
|
869
|
+
return result;
|
|
870
|
+
}
|
|
926
871
|
/**
|
|
927
|
-
* Example: `series.
|
|
928
|
-
*
|
|
872
|
+
* Example: `series.diff("requests")`.
|
|
873
|
+
* Computes per-event differences for the specified numeric columns.
|
|
874
|
+
* Non-specified columns pass through unchanged. The first event gets
|
|
875
|
+
* `undefined` in affected columns unless `{ drop: true }` is passed,
|
|
876
|
+
* which removes the first event entirely.
|
|
929
877
|
*
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
* overlap under half-open overlap rules.
|
|
878
|
+
* Example: `series.diff(["requests", "cpu"])`.
|
|
879
|
+
* Multiple columns can be diffed in a single call.
|
|
933
880
|
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
881
|
+
* Example: `series.diff("requests", { drop: true })`.
|
|
882
|
+
* Drops the first event instead of keeping it with undefined values.
|
|
883
|
+
*/
|
|
884
|
+
diff(columns, options) {
|
|
885
|
+
return this.#diffOrRate('diff', columns, options);
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Example: `series.rate("requests")`.
|
|
889
|
+
* Computes the per-second rate of change for the specified numeric columns.
|
|
890
|
+
* Non-specified columns pass through unchanged. The first event gets
|
|
891
|
+
* `undefined` in affected columns unless `{ drop: true }` is passed,
|
|
892
|
+
* which removes the first event entirely.
|
|
936
893
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
* but the default aggregation range is always the source series extent. When a
|
|
940
|
-
* `BoundedSequence` is supplied, its intervals are used directly.
|
|
894
|
+
* Example: `series.rate(["requests", "cpu"])`.
|
|
895
|
+
* Multiple columns can be rated in a single call.
|
|
941
896
|
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
897
|
+
* Example: `series.rate("requests", { drop: true })`.
|
|
898
|
+
* Drops the first event instead of keeping it with undefined values.
|
|
899
|
+
*/
|
|
900
|
+
rate(columns, options) {
|
|
901
|
+
return this.#diffOrRate('rate', columns, options);
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Example: `series.pctChange("requests")`.
|
|
905
|
+
* Computes the percentage change `(curr - prev) / prev` for the specified
|
|
906
|
+
* numeric columns. Non-specified columns pass through unchanged. The first
|
|
907
|
+
* event gets `undefined` in affected columns unless `{ drop: true }` is
|
|
908
|
+
* passed.
|
|
909
|
+
*/
|
|
910
|
+
pctChange(columns, options) {
|
|
911
|
+
return this.#diffOrRate('pctChange', columns, options);
|
|
912
|
+
}
|
|
913
|
+
#diffOrRate(mode, columns, options) {
|
|
914
|
+
const cols = typeof columns === 'string' ? [columns] : columns;
|
|
915
|
+
const drop = options?.drop === true;
|
|
916
|
+
if (cols.length === 0) {
|
|
917
|
+
throw new Error(`${mode}() requires at least one column name`);
|
|
918
|
+
}
|
|
919
|
+
const targetSet = new Set(cols);
|
|
920
|
+
const outSchema = Object.freeze(this.schema.map((col, i) => {
|
|
921
|
+
if (i === 0)
|
|
922
|
+
return col;
|
|
923
|
+
if (targetSet.has(col.name)) {
|
|
924
|
+
return { ...col, kind: 'number', required: false };
|
|
925
|
+
}
|
|
926
|
+
return col;
|
|
927
|
+
}));
|
|
928
|
+
const events = this.events;
|
|
929
|
+
if (events.length === 0) {
|
|
930
|
+
return _a.#fromTrustedEvents(this.name, outSchema, []);
|
|
931
|
+
}
|
|
932
|
+
const resultEvents = [];
|
|
933
|
+
if (!drop) {
|
|
934
|
+
const firstData = { ...events[0].data() };
|
|
935
|
+
for (const col of cols) {
|
|
936
|
+
firstData[col] = undefined;
|
|
937
|
+
}
|
|
938
|
+
resultEvents.push(new Event(events[0].key(), firstData));
|
|
939
|
+
}
|
|
940
|
+
for (let i = 1; i < events.length; i++) {
|
|
941
|
+
const prev = events[i - 1];
|
|
942
|
+
const curr = events[i];
|
|
943
|
+
const data = { ...curr.data() };
|
|
944
|
+
const dt = mode === 'rate' ? (curr.begin() - prev.begin()) / 1000 : undefined;
|
|
945
|
+
for (const col of cols) {
|
|
946
|
+
const prevVal = prev.data()[col];
|
|
947
|
+
const currVal = data[col];
|
|
948
|
+
if (typeof currVal === 'number' && typeof prevVal === 'number') {
|
|
949
|
+
const delta = currVal - prevVal;
|
|
950
|
+
if (mode === 'pctChange') {
|
|
951
|
+
data[col] = prevVal !== 0 ? delta / prevVal : undefined;
|
|
952
|
+
}
|
|
953
|
+
else if (mode === 'rate') {
|
|
954
|
+
data[col] = dt !== 0 ? delta / dt : undefined;
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
data[col] = delta;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
data[col] = undefined;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
resultEvents.push(new Event(curr.key(), data));
|
|
965
|
+
}
|
|
966
|
+
return _a.#fromTrustedEvents(this.name, outSchema, resultEvents);
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Example: `series.cumulative({ requests: "sum" })`.
|
|
970
|
+
* Computes running accumulations for the specified numeric columns.
|
|
971
|
+
* Non-accumulated columns pass through unchanged.
|
|
972
|
+
*
|
|
973
|
+
* Built-in accumulators: `"sum"`, `"max"`, `"min"`, `"count"`.
|
|
974
|
+
* Custom accumulators: `(acc: number, value: number) => number`.
|
|
975
|
+
*/
|
|
976
|
+
cumulative(spec) {
|
|
977
|
+
const entries = Object.entries(spec);
|
|
978
|
+
if (entries.length === 0) {
|
|
979
|
+
throw new Error('cumulative() requires at least one column');
|
|
980
|
+
}
|
|
981
|
+
const targetSet = new Set(entries.map(([name]) => name));
|
|
982
|
+
const outSchema = Object.freeze(this.schema.map((col, i) => {
|
|
983
|
+
if (i === 0)
|
|
984
|
+
return col;
|
|
985
|
+
if (targetSet.has(col.name)) {
|
|
986
|
+
return { ...col, kind: 'number', required: false };
|
|
987
|
+
}
|
|
988
|
+
return col;
|
|
989
|
+
}));
|
|
990
|
+
const events = this.events;
|
|
991
|
+
if (events.length === 0) {
|
|
992
|
+
return _a.#fromTrustedEvents(this.name, outSchema, []);
|
|
993
|
+
}
|
|
994
|
+
const state = new Map();
|
|
995
|
+
for (const [name, reducer] of entries) {
|
|
996
|
+
if (typeof reducer === 'function') {
|
|
997
|
+
const fn = reducer;
|
|
998
|
+
state.set(name, {
|
|
999
|
+
acc: undefined,
|
|
1000
|
+
apply: (acc, v) => (acc === undefined ? v : fn(acc, v)),
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
switch (reducer) {
|
|
1005
|
+
case 'sum':
|
|
1006
|
+
state.set(name, {
|
|
1007
|
+
acc: undefined,
|
|
1008
|
+
apply: (acc, v) => (acc ?? 0) + v,
|
|
1009
|
+
});
|
|
1010
|
+
break;
|
|
1011
|
+
case 'count':
|
|
1012
|
+
state.set(name, { acc: undefined, apply: (acc) => (acc ?? 0) + 1 });
|
|
1013
|
+
break;
|
|
1014
|
+
case 'max':
|
|
1015
|
+
state.set(name, {
|
|
1016
|
+
acc: undefined,
|
|
1017
|
+
apply: (acc, v) => (acc === undefined || v > acc ? v : acc),
|
|
1018
|
+
});
|
|
1019
|
+
break;
|
|
1020
|
+
case 'min':
|
|
1021
|
+
state.set(name, {
|
|
1022
|
+
acc: undefined,
|
|
1023
|
+
apply: (acc, v) => (acc === undefined || v < acc ? v : acc),
|
|
1024
|
+
});
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
const resultEvents = [];
|
|
1030
|
+
for (const event of events) {
|
|
1031
|
+
const data = { ...event.data() };
|
|
1032
|
+
for (const [name, s] of state) {
|
|
1033
|
+
const raw = data[name];
|
|
1034
|
+
if (typeof raw === 'number') {
|
|
1035
|
+
s.acc = s.apply(s.acc, raw);
|
|
1036
|
+
data[name] = s.acc;
|
|
1037
|
+
}
|
|
1038
|
+
else {
|
|
1039
|
+
data[name] = s.acc;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
resultEvents.push(new Event(event.key(), data));
|
|
1043
|
+
}
|
|
1044
|
+
return _a.#fromTrustedEvents(this.name, outSchema, resultEvents);
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Example: `series.shift("value", 1)`.
|
|
1048
|
+
* Lags column values by N events (positive N) or leads them (negative N).
|
|
1049
|
+
* Vacated positions get `undefined`.
|
|
1050
|
+
*/
|
|
1051
|
+
shift(columns, n) {
|
|
1052
|
+
const cols = typeof columns === 'string' ? [columns] : columns;
|
|
1053
|
+
if (cols.length === 0) {
|
|
1054
|
+
throw new Error('shift() requires at least one column name');
|
|
1055
|
+
}
|
|
1056
|
+
if (!Number.isInteger(n)) {
|
|
1057
|
+
throw new Error('shift() requires an integer offset');
|
|
1058
|
+
}
|
|
1059
|
+
const targetSet = new Set(cols);
|
|
1060
|
+
const outSchema = Object.freeze(this.schema.map((col, i) => {
|
|
1061
|
+
if (i === 0)
|
|
1062
|
+
return col;
|
|
1063
|
+
if (targetSet.has(col.name)) {
|
|
1064
|
+
return { ...col, kind: 'number', required: false };
|
|
1065
|
+
}
|
|
1066
|
+
return col;
|
|
1067
|
+
}));
|
|
1068
|
+
const events = this.events;
|
|
1069
|
+
if (events.length === 0) {
|
|
1070
|
+
return _a.#fromTrustedEvents(this.name, outSchema, []);
|
|
1071
|
+
}
|
|
1072
|
+
const resultEvents = [];
|
|
1073
|
+
for (let i = 0; i < events.length; i++) {
|
|
1074
|
+
const data = { ...events[i].data() };
|
|
1075
|
+
const srcIdx = i - n;
|
|
1076
|
+
for (const col of cols) {
|
|
1077
|
+
if (srcIdx >= 0 && srcIdx < events.length) {
|
|
1078
|
+
data[col] = events[srcIdx].data()[col];
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
data[col] = undefined;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
resultEvents.push(new Event(events[i].key(), data));
|
|
1085
|
+
}
|
|
1086
|
+
return _a.#fromTrustedEvents(this.name, outSchema, resultEvents);
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Example: `series.fill("hold")`.
|
|
1090
|
+
* Fills `undefined` values using the given strategy for all payload columns.
|
|
944
1091
|
*
|
|
945
|
-
*
|
|
946
|
-
*
|
|
1092
|
+
* Example: `series.fill({ cpu: "linear", host: "hold" })`.
|
|
1093
|
+
* Per-column fill strategies. Unmentioned columns are left as-is.
|
|
1094
|
+
* Strategy names: `"hold"` (forward fill), `"linear"` (time-interpolated),
|
|
1095
|
+
* `"zero"` (fill with 0). A non-string value is used as a literal fill value.
|
|
947
1096
|
*
|
|
948
|
-
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
* throw new Error("empty series");
|
|
952
|
-
* }
|
|
1097
|
+
* Example: `series.fill("hold", { limit: 3 })`.
|
|
1098
|
+
* Caps consecutive fills per column. After `limit` consecutive fills, further
|
|
1099
|
+
* `undefined` values are left as-is until a real value resets the counter.
|
|
953
1100
|
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
* { value: "avg" },
|
|
957
|
-
* );
|
|
958
|
-
* ```
|
|
1101
|
+
* `"linear"` requires known values on both sides of a gap to interpolate.
|
|
1102
|
+
* Leading and trailing `undefined` runs are left unfilled.
|
|
959
1103
|
*/
|
|
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
|
-
});
|
|
1104
|
+
fill(strategy, options) {
|
|
1105
|
+
if (this.events.length === 0) {
|
|
1106
|
+
return this;
|
|
986
1107
|
}
|
|
987
|
-
const
|
|
988
|
-
const
|
|
989
|
-
if (
|
|
990
|
-
const
|
|
991
|
-
name:
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1108
|
+
const colNames = this.schema.slice(1).map((c) => c.name);
|
|
1109
|
+
const specs = new Map();
|
|
1110
|
+
if (typeof strategy === 'string') {
|
|
1111
|
+
for (const name of colNames) {
|
|
1112
|
+
specs.set(name, { mode: strategy });
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
const strategies = new Set([
|
|
1117
|
+
'hold',
|
|
1118
|
+
'bfill',
|
|
1119
|
+
'linear',
|
|
1120
|
+
'zero',
|
|
1121
|
+
]);
|
|
1122
|
+
for (const [name, spec] of Object.entries(strategy)) {
|
|
1123
|
+
if (typeof spec === 'string' && strategies.has(spec)) {
|
|
1124
|
+
specs.set(name, { mode: spec });
|
|
1000
1125
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1126
|
+
else {
|
|
1127
|
+
specs.set(name, { mode: 'literal', value: spec });
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
const limit = options?.limit;
|
|
1132
|
+
const n = this.events.length;
|
|
1133
|
+
const columns = {};
|
|
1134
|
+
for (const name of colNames) {
|
|
1135
|
+
columns[name] = new Array(n);
|
|
1136
|
+
}
|
|
1137
|
+
for (let i = 0; i < n; i++) {
|
|
1138
|
+
const data = this.events[i].data();
|
|
1139
|
+
for (const name of colNames) {
|
|
1140
|
+
columns[name][i] = data[name];
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
const times = new Array(n);
|
|
1144
|
+
for (let i = 0; i < n; i++) {
|
|
1145
|
+
times[i] = this.events[i].begin();
|
|
1146
|
+
}
|
|
1147
|
+
for (const [name, spec] of specs) {
|
|
1148
|
+
const col = columns[name];
|
|
1149
|
+
if (!col)
|
|
1150
|
+
continue;
|
|
1151
|
+
switch (spec.mode) {
|
|
1152
|
+
case 'hold': {
|
|
1153
|
+
let last;
|
|
1154
|
+
let consecutive = 0;
|
|
1155
|
+
for (let i = 0; i < n; i++) {
|
|
1156
|
+
if (col[i] !== undefined) {
|
|
1157
|
+
last = col[i];
|
|
1158
|
+
consecutive = 0;
|
|
1159
|
+
}
|
|
1160
|
+
else if (last !== undefined) {
|
|
1161
|
+
consecutive++;
|
|
1162
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1163
|
+
col[i] = last;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1008
1166
|
}
|
|
1009
|
-
|
|
1167
|
+
break;
|
|
1010
1168
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1169
|
+
case 'bfill': {
|
|
1170
|
+
let next;
|
|
1171
|
+
let consecutive = 0;
|
|
1172
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
1173
|
+
if (col[i] !== undefined) {
|
|
1174
|
+
next = col[i];
|
|
1175
|
+
consecutive = 0;
|
|
1176
|
+
}
|
|
1177
|
+
else if (next !== undefined) {
|
|
1178
|
+
consecutive++;
|
|
1179
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1180
|
+
col[i] = next;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
break;
|
|
1185
|
+
}
|
|
1186
|
+
case 'zero': {
|
|
1187
|
+
let consecutive = 0;
|
|
1188
|
+
for (let i = 0; i < n; i++) {
|
|
1189
|
+
if (col[i] !== undefined) {
|
|
1190
|
+
consecutive = 0;
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
consecutive++;
|
|
1194
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1195
|
+
col[i] = 0;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
break;
|
|
1200
|
+
}
|
|
1201
|
+
case 'literal': {
|
|
1202
|
+
let consecutive = 0;
|
|
1203
|
+
for (let i = 0; i < n; i++) {
|
|
1204
|
+
if (col[i] !== undefined) {
|
|
1205
|
+
consecutive = 0;
|
|
1206
|
+
}
|
|
1207
|
+
else {
|
|
1208
|
+
consecutive++;
|
|
1209
|
+
if (limit === undefined || consecutive <= limit) {
|
|
1210
|
+
col[i] = spec.value;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
break;
|
|
1215
|
+
}
|
|
1216
|
+
case 'linear': {
|
|
1217
|
+
let gapStart = -1;
|
|
1218
|
+
for (let i = 0; i < n; i++) {
|
|
1219
|
+
if (col[i] !== undefined) {
|
|
1220
|
+
if (gapStart >= 0 && gapStart > 0) {
|
|
1221
|
+
const before = col[gapStart - 1];
|
|
1222
|
+
const after = col[i];
|
|
1223
|
+
const t0 = times[gapStart - 1];
|
|
1224
|
+
const t1 = times[i];
|
|
1225
|
+
const span = t1 - t0;
|
|
1226
|
+
const gapLen = i - gapStart;
|
|
1227
|
+
for (let j = gapStart; j < i; j++) {
|
|
1228
|
+
const fillIndex = j - gapStart + 1;
|
|
1229
|
+
if (limit !== undefined && fillIndex > limit)
|
|
1230
|
+
break;
|
|
1231
|
+
if (span === 0) {
|
|
1232
|
+
col[j] = before;
|
|
1233
|
+
}
|
|
1234
|
+
else {
|
|
1235
|
+
const ratio = (times[j] - t0) / span;
|
|
1236
|
+
col[j] = before + (after - before) * ratio;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
gapStart = -1;
|
|
1241
|
+
}
|
|
1242
|
+
else if (gapStart < 0) {
|
|
1243
|
+
gapStart = i;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
break;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1022
1249
|
}
|
|
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
|
-
});
|
|
1250
|
+
const resultEvents = [];
|
|
1251
|
+
for (let i = 0; i < n; i++) {
|
|
1252
|
+
const data = {};
|
|
1253
|
+
for (const name of colNames) {
|
|
1254
|
+
data[name] = columns[name][i];
|
|
1255
|
+
}
|
|
1256
|
+
resultEvents.push(new Event(this.events[i].key(), data));
|
|
1257
|
+
}
|
|
1258
|
+
return _a.#fromTrustedEvents(this.name, this.schema, resultEvents);
|
|
1040
1259
|
}
|
|
1041
1260
|
rolling(sequenceOrWindow, windowOrMapping, mappingOrOptions, maybeOptions = {}) {
|
|
1042
1261
|
const buildResultColumns = () => this.schema
|
|
@@ -1092,7 +1311,7 @@ export class TimeSeries {
|
|
|
1092
1311
|
...buildResultColumns(),
|
|
1093
1312
|
]);
|
|
1094
1313
|
if (!range) {
|
|
1095
|
-
return new
|
|
1314
|
+
return new _a({
|
|
1096
1315
|
name: this.name,
|
|
1097
1316
|
schema: resultSchema,
|
|
1098
1317
|
rows: [],
|
|
@@ -1103,16 +1322,16 @@ export class TimeSeries {
|
|
|
1103
1322
|
const anchor = sampleTime(bucket, sample);
|
|
1104
1323
|
const contributors = this.events.filter((candidate) => anchorInWindow(candidate.begin(), anchor));
|
|
1105
1324
|
const aggregated = resultSchema.slice(1).map((column) => {
|
|
1106
|
-
const
|
|
1325
|
+
const reducer = mapping[column.name];
|
|
1107
1326
|
const values = contributors.map((candidate) => {
|
|
1108
1327
|
const data = candidate.data();
|
|
1109
1328
|
return data[column.name];
|
|
1110
1329
|
});
|
|
1111
|
-
return
|
|
1330
|
+
return applyAggregateReducer(reducer, values);
|
|
1112
1331
|
});
|
|
1113
1332
|
return Object.freeze([bucket, ...aggregated]);
|
|
1114
1333
|
});
|
|
1115
|
-
return new
|
|
1334
|
+
return new _a({
|
|
1116
1335
|
name: this.name,
|
|
1117
1336
|
schema: resultSchema,
|
|
1118
1337
|
rows: resultRows,
|
|
@@ -1123,7 +1342,12 @@ export class TimeSeries {
|
|
|
1123
1342
|
this.schema[0],
|
|
1124
1343
|
...resultColumns,
|
|
1125
1344
|
]);
|
|
1126
|
-
const reducerStates = resultColumns.map((column) =>
|
|
1345
|
+
const reducerStates = resultColumns.map((column) => {
|
|
1346
|
+
const reducer = mapping[column.name];
|
|
1347
|
+
return isBuiltInAggregateReducer(reducer)
|
|
1348
|
+
? createRollingReducerState(reducer)
|
|
1349
|
+
: null;
|
|
1350
|
+
});
|
|
1127
1351
|
const beginTimes = this.events.map((event) => event.begin());
|
|
1128
1352
|
const resultRows = new Array(this.events.length);
|
|
1129
1353
|
let windowStart = 0;
|
|
@@ -1131,19 +1355,38 @@ export class TimeSeries {
|
|
|
1131
1355
|
const addEvent = (index) => {
|
|
1132
1356
|
const event = this.events[index];
|
|
1133
1357
|
const data = event.data();
|
|
1134
|
-
for (let
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1358
|
+
for (let i = 0; i < reducerStates.length; i++) {
|
|
1359
|
+
const state = reducerStates[i];
|
|
1360
|
+
if (state) {
|
|
1361
|
+
const column = resultColumns[i];
|
|
1362
|
+
state.add(index, data[column.name]);
|
|
1363
|
+
}
|
|
1137
1364
|
}
|
|
1138
1365
|
};
|
|
1139
1366
|
const removeEvent = (index) => {
|
|
1140
1367
|
const event = this.events[index];
|
|
1141
1368
|
const data = event.data();
|
|
1142
|
-
for (let
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1369
|
+
for (let i = 0; i < reducerStates.length; i++) {
|
|
1370
|
+
const state = reducerStates[i];
|
|
1371
|
+
if (state) {
|
|
1372
|
+
const column = resultColumns[i];
|
|
1373
|
+
state.remove(index, data[column.name]);
|
|
1374
|
+
}
|
|
1145
1375
|
}
|
|
1146
1376
|
};
|
|
1377
|
+
const snapshotWindow = () => resultColumns.map((column, i) => {
|
|
1378
|
+
const state = reducerStates[i];
|
|
1379
|
+
if (state)
|
|
1380
|
+
return state.snapshot();
|
|
1381
|
+
const reducer = mapping[column.name];
|
|
1382
|
+
const values = this.events
|
|
1383
|
+
.slice(windowStart, windowEnd)
|
|
1384
|
+
.map((event) => {
|
|
1385
|
+
const data = event.data();
|
|
1386
|
+
return data[column.name];
|
|
1387
|
+
});
|
|
1388
|
+
return applyAggregateReducer(reducer, values);
|
|
1389
|
+
});
|
|
1147
1390
|
if (alignment === 'trailing') {
|
|
1148
1391
|
for (let groupStart = 0; groupStart < this.events.length;) {
|
|
1149
1392
|
const anchor = beginTimes[groupStart];
|
|
@@ -1163,7 +1406,7 @@ export class TimeSeries {
|
|
|
1163
1406
|
removeEvent(windowStart);
|
|
1164
1407
|
windowStart += 1;
|
|
1165
1408
|
}
|
|
1166
|
-
const aggregated =
|
|
1409
|
+
const aggregated = snapshotWindow();
|
|
1167
1410
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
1168
1411
|
resultRows[index] = Object.freeze([
|
|
1169
1412
|
this.events[index].key(),
|
|
@@ -1193,7 +1436,7 @@ export class TimeSeries {
|
|
|
1193
1436
|
addEvent(windowEnd);
|
|
1194
1437
|
windowEnd += 1;
|
|
1195
1438
|
}
|
|
1196
|
-
const aggregated =
|
|
1439
|
+
const aggregated = snapshotWindow();
|
|
1197
1440
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
1198
1441
|
resultRows[index] = Object.freeze([
|
|
1199
1442
|
this.events[index].key(),
|
|
@@ -1224,7 +1467,7 @@ export class TimeSeries {
|
|
|
1224
1467
|
addEvent(windowEnd);
|
|
1225
1468
|
windowEnd += 1;
|
|
1226
1469
|
}
|
|
1227
|
-
const aggregated =
|
|
1470
|
+
const aggregated = snapshotWindow();
|
|
1228
1471
|
for (let index = groupStart; index < groupEnd; index++) {
|
|
1229
1472
|
resultRows[index] = Object.freeze([
|
|
1230
1473
|
this.events[index].key(),
|
|
@@ -1234,7 +1477,7 @@ export class TimeSeries {
|
|
|
1234
1477
|
groupStart = groupEnd;
|
|
1235
1478
|
}
|
|
1236
1479
|
}
|
|
1237
|
-
return new
|
|
1480
|
+
return new _a({
|
|
1238
1481
|
name: this.name,
|
|
1239
1482
|
schema: resultSchema,
|
|
1240
1483
|
rows: resultRows,
|
|
@@ -1299,7 +1542,7 @@ export class TimeSeries {
|
|
|
1299
1542
|
.map((nextColumn) => nextEvent.data()[nextColumn.name]),
|
|
1300
1543
|
]);
|
|
1301
1544
|
});
|
|
1302
|
-
return new
|
|
1545
|
+
return new _a({
|
|
1303
1546
|
name: this.name,
|
|
1304
1547
|
schema: resultSchema,
|
|
1305
1548
|
rows: resultRows,
|
|
@@ -1337,7 +1580,7 @@ export class TimeSeries {
|
|
|
1337
1580
|
.map((nextColumn) => nextEvent.data()[nextColumn.name]),
|
|
1338
1581
|
]);
|
|
1339
1582
|
});
|
|
1340
|
-
return new
|
|
1583
|
+
return new _a({
|
|
1341
1584
|
name: this.name,
|
|
1342
1585
|
schema: resultSchema,
|
|
1343
1586
|
rows: resultRows,
|
|
@@ -1431,7 +1674,7 @@ export class TimeSeries {
|
|
|
1431
1674
|
.map((nextColumn) => nextEvent.data()[nextColumn.name]),
|
|
1432
1675
|
]);
|
|
1433
1676
|
});
|
|
1434
|
-
return new
|
|
1677
|
+
return new _a({
|
|
1435
1678
|
name: this.name,
|
|
1436
1679
|
schema: resultSchema,
|
|
1437
1680
|
rows: resultRows,
|
|
@@ -1439,11 +1682,11 @@ export class TimeSeries {
|
|
|
1439
1682
|
}
|
|
1440
1683
|
/** Example: `series.slice(0, 10)`. Returns a positional half-open slice of the series. */
|
|
1441
1684
|
slice(beginIndex, endIndex) {
|
|
1442
|
-
return
|
|
1685
|
+
return _a.#fromTrustedEvents(this.name, this.schema, this.events.slice(beginIndex, endIndex));
|
|
1443
1686
|
}
|
|
1444
1687
|
/** Example: `series.filter(event => event.get("active"))`. Returns a new series containing only events that match the predicate. */
|
|
1445
1688
|
filter(predicate) {
|
|
1446
|
-
return
|
|
1689
|
+
return _a.#fromTrustedEvents(this.name, this.schema, this.events.filter((event, index) => predicate(event, index)));
|
|
1447
1690
|
}
|
|
1448
1691
|
/** Example: `series.find(event => event.get("value") > 0)`. Returns the first event that matches the predicate, if any. */
|
|
1449
1692
|
find(predicate) {
|
|
@@ -1564,7 +1807,7 @@ export class TimeSeries {
|
|
|
1564
1807
|
const trimmedEvents = this.events
|
|
1565
1808
|
.map((event) => event.trim(range))
|
|
1566
1809
|
.filter((event) => event !== undefined);
|
|
1567
|
-
return
|
|
1810
|
+
return _a.#fromTrustedEvents(this.name, this.schema, trimmedEvents);
|
|
1568
1811
|
}
|
|
1569
1812
|
/** Example: `series.before(Date.now())`. Returns the events ending strictly before the supplied temporal boundary. */
|
|
1570
1813
|
before(boundary) {
|
|
@@ -1596,7 +1839,7 @@ export class TimeSeries {
|
|
|
1596
1839
|
const selectedEvent = event.select(...keys);
|
|
1597
1840
|
return selectedEvent;
|
|
1598
1841
|
});
|
|
1599
|
-
return
|
|
1842
|
+
return _a.#fromTrustedEvents(this.name, resultSchema, resultEvents);
|
|
1600
1843
|
}
|
|
1601
1844
|
/** Example: `series.rename({ cpu: "usage" })`. Returns a new series with payload field names renamed according to the supplied mapping. */
|
|
1602
1845
|
rename(mapping) {
|
|
@@ -1614,7 +1857,7 @@ export class TimeSeries {
|
|
|
1614
1857
|
const renamedEvent = event.rename(mapping);
|
|
1615
1858
|
return renamedEvent;
|
|
1616
1859
|
});
|
|
1617
|
-
return
|
|
1860
|
+
return _a.#fromTrustedEvents(this.name, resultSchema, resultEvents);
|
|
1618
1861
|
}
|
|
1619
1862
|
collapse(keys, output, reducer, options) {
|
|
1620
1863
|
const nextEvents = this.events.map((event) => {
|
|
@@ -1642,54 +1885,138 @@ export class TimeSeries {
|
|
|
1642
1885
|
: 'string',
|
|
1643
1886
|
},
|
|
1644
1887
|
]);
|
|
1645
|
-
return
|
|
1888
|
+
return _a.#fromTrustedEvents(this.name, resultSchema, nextEvents);
|
|
1646
1889
|
}
|
|
1647
1890
|
/** Example: `series.length`. Returns the number of events in the series. */
|
|
1648
1891
|
get length() {
|
|
1649
1892
|
return this.events.length;
|
|
1650
1893
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1894
|
+
}
|
|
1895
|
+
_a = TimeSeries;
|
|
1896
|
+
function aggregateInternal(series, sequence, mapping, options = {}) {
|
|
1897
|
+
const range = options.range ?? series.timeRange();
|
|
1898
|
+
const aggregateColumns = normalizeAggregateColumns(series.schema, mapping);
|
|
1899
|
+
const resultSchema = Object.freeze([
|
|
1900
|
+
{ name: 'interval', kind: 'interval' },
|
|
1901
|
+
...aggregateColumns.map((column) => ({
|
|
1902
|
+
name: column.output,
|
|
1903
|
+
kind: column.kind,
|
|
1904
|
+
required: false,
|
|
1905
|
+
})),
|
|
1906
|
+
]);
|
|
1907
|
+
if (!range) {
|
|
1908
|
+
return new TimeSeries({
|
|
1909
|
+
name: series.name,
|
|
1910
|
+
schema: resultSchema,
|
|
1911
|
+
rows: [],
|
|
1912
|
+
});
|
|
1654
1913
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1914
|
+
const buckets = toBoundedSequence(sequence, range, 'begin').intervals();
|
|
1915
|
+
const columns = aggregateColumns;
|
|
1916
|
+
if (isTimeKeyed(series)) {
|
|
1917
|
+
const builtInOnly = columns.every((column) => isBuiltInAggregateReducer(column.reducer));
|
|
1918
|
+
let eventIndex = 0;
|
|
1919
|
+
const resultRows = buckets.map((bucket) => {
|
|
1920
|
+
const states = builtInOnly
|
|
1921
|
+
? columns.map((column) => createAggregateBucketState(column.reducer))
|
|
1922
|
+
: undefined;
|
|
1923
|
+
while (eventIndex < series.events.length &&
|
|
1924
|
+
series.events[eventIndex].begin() < bucket.begin()) {
|
|
1925
|
+
eventIndex += 1;
|
|
1662
1926
|
}
|
|
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;
|
|
1927
|
+
const bucketStart = eventIndex;
|
|
1928
|
+
let scanIndex = bucketStart;
|
|
1929
|
+
while (scanIndex < series.events.length &&
|
|
1930
|
+
series.events[scanIndex].begin() < bucket.end()) {
|
|
1931
|
+
if (states) {
|
|
1932
|
+
const data = series.events[scanIndex].data();
|
|
1933
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1934
|
+
const column = columns[index];
|
|
1935
|
+
states[index].add(data[column.source]);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
scanIndex += 1;
|
|
1939
|
+
}
|
|
1940
|
+
eventIndex = scanIndex;
|
|
1941
|
+
if (states) {
|
|
1942
|
+
return Object.freeze([
|
|
1943
|
+
bucket,
|
|
1944
|
+
...states.map((state) => state.snapshot()),
|
|
1945
|
+
]);
|
|
1689
1946
|
}
|
|
1690
|
-
|
|
1947
|
+
const contributors = series.events.slice(bucketStart, scanIndex);
|
|
1948
|
+
const aggregated = columns.map((column) => {
|
|
1949
|
+
const values = contributors.map((event) => {
|
|
1950
|
+
const data = event.data();
|
|
1951
|
+
return data[column.source];
|
|
1952
|
+
});
|
|
1953
|
+
return applyAggregateReducer(column.reducer, values);
|
|
1954
|
+
});
|
|
1955
|
+
return Object.freeze([bucket, ...aggregated]);
|
|
1956
|
+
});
|
|
1957
|
+
return new TimeSeries({
|
|
1958
|
+
name: series.name,
|
|
1959
|
+
schema: resultSchema,
|
|
1960
|
+
rows: resultRows,
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
const resultRows = buckets.map((bucket) => {
|
|
1964
|
+
const contributors = series.events.filter((event) => bucketOverlapsHalfOpen(bucket, event.key()));
|
|
1965
|
+
const aggregated = columns.map((column) => {
|
|
1966
|
+
const values = contributors.map((event) => {
|
|
1967
|
+
const data = event.data();
|
|
1968
|
+
return data[column.source];
|
|
1969
|
+
});
|
|
1970
|
+
return applyAggregateReducer(column.reducer, values);
|
|
1971
|
+
});
|
|
1972
|
+
return Object.freeze([bucket, ...aggregated]);
|
|
1973
|
+
});
|
|
1974
|
+
return new TimeSeries({
|
|
1975
|
+
name: series.name,
|
|
1976
|
+
schema: resultSchema,
|
|
1977
|
+
rows: resultRows,
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
function alignHoldAt(series, t) {
|
|
1981
|
+
const event = series.atOrBefore(new Time(t));
|
|
1982
|
+
return (event?.data() ?? {});
|
|
1983
|
+
}
|
|
1984
|
+
function alignLinearAt(series, t, valueColumns, cursor) {
|
|
1985
|
+
const events = series.events;
|
|
1986
|
+
const hasCursor = cursor !== undefined;
|
|
1987
|
+
let index = hasCursor ? cursor.index : series.bisect(t);
|
|
1988
|
+
if (hasCursor) {
|
|
1989
|
+
while (index < events.length && events[index].begin() < t) {
|
|
1990
|
+
index += 1;
|
|
1691
1991
|
}
|
|
1692
|
-
|
|
1992
|
+
cursor.index = index;
|
|
1993
|
+
}
|
|
1994
|
+
if (index < events.length && events[index].begin() === t) {
|
|
1995
|
+
return events[index].data();
|
|
1996
|
+
}
|
|
1997
|
+
if (index === 0) {
|
|
1998
|
+
return {};
|
|
1999
|
+
}
|
|
2000
|
+
const previous = events[index - 1];
|
|
2001
|
+
const next = events[index];
|
|
2002
|
+
if (!next || previous.begin() === next.begin()) {
|
|
2003
|
+
return previous.data();
|
|
2004
|
+
}
|
|
2005
|
+
const ratio = (t - previous.begin()) / (next.begin() - previous.begin());
|
|
2006
|
+
const result = {};
|
|
2007
|
+
const previousData = previous.data();
|
|
2008
|
+
const nextData = next.data();
|
|
2009
|
+
for (const column of valueColumns) {
|
|
2010
|
+
const previousValue = previousData[column.name];
|
|
2011
|
+
const nextValue = nextData[column.name];
|
|
2012
|
+
if (column.kind === 'number' &&
|
|
2013
|
+
typeof previousValue === 'number' &&
|
|
2014
|
+
typeof nextValue === 'number') {
|
|
2015
|
+
result[column.name] = previousValue + (nextValue - previousValue) * ratio;
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
result[column.name] = previousValue;
|
|
1693
2019
|
}
|
|
2020
|
+
return result;
|
|
1694
2021
|
}
|
|
1695
2022
|
//# sourceMappingURL=TimeSeries.js.map
|