Skip to content

Commit 3444c8d

Browse files
Make input more type-relaxed; Make LTOB and LTTB methods generic
1 parent 9f0be0c commit 3444c8d

4 files changed

Lines changed: 34 additions & 63 deletions

File tree

src/methods/LTOB.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DataPoint, NormalizedDataPoint } from "../types";
22
import { normalizeDataPoints, calculateTriangleArea } from "../utils";
33

44
// Largest triangle three buckets data downsampling algorithm implementation
5-
export default function LTOB(data: DataPoint[], desiredLength: number): DataPoint[] {
5+
export default function LTOB<T extends DataPoint>(data: T[], desiredLength: number): T[] {
66
if (desiredLength < 0) {
77
throw new Error(`Supplied negative desiredLength parameter to LTOB: ${desiredLength}`);
88
}
@@ -18,7 +18,7 @@ export default function LTOB(data: DataPoint[], desiredLength: number): DataPoin
1818
// - threshold is (length, Inifnity)
1919
const bucketSize: number = Math.ceil(length / desiredLength);
2020
const normalizedData: NormalizedDataPoint[] = normalizeDataPoints(data);
21-
const sampledData: DataPoint[] = [data[0]];
21+
const sampledData: T[] = [data[0]];
2222

2323
for (let bucket: number = 1; bucket < desiredLength - 1; bucket++) {
2424
const startIndex: number = bucket * bucketSize;

src/methods/LTTB.ts

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DataPoint, NormalizedDataPoint } from "../types";
22
import { normalizeDataPoints, calculateTriangleArea, calculateAverageDataPoint } from "../utils";
33

44
// Largest triangle three buckets data downsampling algorithm implementation
5-
export default function LTTB(data: DataPoint[], desiredLength: number): DataPoint[] {
5+
export default function LTTB<T extends DataPoint>(data: T[], desiredLength: number): T[] {
66
if (desiredLength < 0) {
77
throw new Error(`Supplied negative desiredLength parameter to LTTB: ${desiredLength}`);
88
}
@@ -16,10 +16,9 @@ export default function LTTB(data: DataPoint[], desiredLength: number): DataPoin
1616
//
1717
// - length is [2, Infinity)
1818
// - threshold is (length, Inifnity)
19-
const sampledLength: number = desiredLength - 2;
2019
const bucketSize: number = Math.ceil(length / desiredLength);
2120
const normalizedData: NormalizedDataPoint[] = normalizeDataPoints(data);
22-
const sampledData: DataPoint[] = [data[0]];
21+
const sampledData: T[] = [data[0]];
2322

2423
let lastSelectedDataPoint: NormalizedDataPoint = normalizedData[0];
2524
for (let bucket: number = 1; bucket < desiredLength - 1; bucket++) {
@@ -49,55 +48,4 @@ export default function LTTB(data: DataPoint[], desiredLength: number): DataPoin
4948
sampledData.push(data[length - 1]);
5049

5150
return sampledData;
52-
53-
54-
55-
56-
57-
58-
59-
let a: number = 0;
60-
const sampled: DataPoint[] = [data[0]];
61-
for (let i: number = 0; i < sampledLength; i++) {
62-
const averageXStartIndex: number = Math.floor((i + 1) * bucketSize) + 1;
63-
const averageXEndIndex: number = Math.min(length, Math.floor((i + 2) * bucketSize) + 1);
64-
65-
let averageX: number = 0;
66-
let averageY: number = 0;
67-
for (let j: number = averageXStartIndex; j < averageXEndIndex; j++) {
68-
averageX += normalizedData[j][0];
69-
averageY += normalizedData[j][1];
70-
}
71-
72-
const averageXSpan: number = averageXEndIndex - averageXStartIndex;
73-
averageX /= averageXSpan;
74-
averageY /= averageXSpan;
75-
const averageDataPoint: NormalizedDataPoint = [averageX, averageY];
76-
77-
const rangeXStart = Math.floor(i * bucketSize) + 1;
78-
const rangeXEnd = Math.floor((i + 1) * bucketSize) + 1;
79-
80-
const dataPointA: NormalizedDataPoint = normalizedData[a];
81-
let maxArea: number = -1;
82-
let maxAreaIndex: number;
83-
84-
for (let k: number = rangeXStart; k < rangeXEnd; k++) {
85-
const dataPointK = normalizedData[k];
86-
const area = calculateTriangleArea(dataPointA, dataPointK, averageDataPoint);
87-
88-
if (area > maxArea) {
89-
maxArea = area;
90-
maxAreaIndex = k;
91-
}
92-
}
93-
94-
const maxAreaDataPoint: DataPoint = data[maxAreaIndex];
95-
96-
a = maxAreaIndex;
97-
sampled.push(maxAreaDataPoint);
98-
}
99-
100-
sampled.push(data[length - 1]);
101-
102-
return sampled;
10351
}

src/types.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
export interface DataPoint {
2-
x: number | Date;
3-
y: number;
1+
export type X = number | Date;
2+
export type Y = number;
3+
4+
export type TupleDataPoint = [X, Y];
5+
6+
export interface XYDataPoint {
7+
x: X;
8+
y: Y;
49
}
510

11+
export type DataPoint = TupleDataPoint | XYDataPoint;
12+
export type DataPoints = TupleDataPoint[] | XYDataPoint[];
13+
614
export type NormalizedDataPoint = [number, number];

src/utils.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1-
import { DataPoint, NormalizedDataPoint } from "./types";
1+
import { DataPoint, NormalizedDataPoint, XYDataPoint, X, TupleDataPoint } from "./types";
2+
3+
const isTupleDataPoint = (dataPoint: DataPoint): dataPoint is TupleDataPoint => {
4+
return Array.isArray(dataPoint);
5+
}
6+
7+
const isXYDataPoint = (dataPoint: DataPoint): dataPoint is XYDataPoint => {
8+
return !!dataPoint && "x" in dataPoint && "y" in dataPoint;
9+
}
10+
11+
const normalizeX = (x: X): number => x instanceof Date ? x.getTime() : x;
212

313
export function normalizeDataPoint(dataPoint: DataPoint): NormalizedDataPoint {
414
if (!dataPoint) return undefined;
515

6-
const x: number = dataPoint.x instanceof Date ? dataPoint.x.getTime() : dataPoint.x;
7-
const y: number = dataPoint.y;
16+
if (isXYDataPoint(dataPoint)) {
17+
return [normalizeX(dataPoint.x), dataPoint.y];
18+
}
19+
20+
if (isTupleDataPoint(dataPoint)) {
21+
return [normalizeX(dataPoint[0]), dataPoint[1]];
22+
}
823

9-
return [x, y];
24+
throw new Error(`Invalid data point format supplied: ${JSON.stringify(dataPoint)}`);
1025
}
1126

1227
export function normalizeDataPoints(dataPoints: DataPoint[]): NormalizedDataPoint[] {

0 commit comments

Comments
 (0)