Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/data/DataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,75 @@ class DataStore {
return target;
}

/**
* Large data down sampling using min-max
* @param {string} valueDimension
* @param {number} rate
*/
minmaxDownSample(
valueDimension: DimensionIndex,
rate: number
): DataStore {
const target = this.clone([valueDimension], true);
const targetStorage = target._chunks;

const frameSize = Math.floor(1 / rate);

const dimStore = targetStorage[valueDimension];
const len = this.count();

// Each frame results in 2 data points, one for min and one for max
const newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize) * 2);

let offset = 0;
for (let i = 0; i < len; i += frameSize) {
let minIndex = i;
let minValue = dimStore[this.getRawIndex(minIndex)];
let maxIndex = i;
let maxValue = dimStore[this.getRawIndex(maxIndex)];

let thisFrameSize = frameSize;
// Handle final smaller frame
if (i + frameSize > len) {
thisFrameSize = len - i;
}
// Determine min and max within the current frame
for (let k = 0; k < thisFrameSize; k++) {
const rawIndex = this.getRawIndex(i + k);
const value = dimStore[rawIndex];

if (value < minValue) {
minValue = value;
minIndex = i + k;
}
if (value > maxValue) {
maxValue = value;
maxIndex = i + k;
}
}

const rawMinIndex = this.getRawIndex(minIndex);
const rawMaxIndex = this.getRawIndex(maxIndex);

// Set the order of the min and max values, based on their ordering in the frame
if (minIndex < maxIndex) {
newIndices[offset++] = rawMinIndex;
newIndices[offset++] = rawMaxIndex;
}
else {
newIndices[offset++] = rawMaxIndex;
newIndices[offset++] = rawMinIndex;
}
}

target._count = offset;
target._indices = newIndices;

target._updateGetRawIdx();

return target;
}


/**
* Large data down sampling on given dimension
Expand Down
21 changes: 19 additions & 2 deletions src/data/SeriesData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,10 @@ class SeriesData<

// Methods that create a new list based on this list should be listed here.
// Notice that those method should `RETURN` the new list.
TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map'] as const;
TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'minmaxDownSample', 'lttbDownSample', 'map'] as const;
// Methods that change indices of this list should be listed here.
CHANGABLE_METHODS = ['filterSelf', 'selectRange'] as const;
DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'] as const;
DOWNSAMPLE_METHODS = ['downSample', 'minmaxDownSample', 'lttbDownSample'] as const;

/**
* @param dimensionsInput.dimensions
Expand Down Expand Up @@ -1098,6 +1098,23 @@ class SeriesData<
return list as SeriesData<HostModel>;
}

/**
* Large data down sampling using min-max
* @param {string} valueDimension
* @param {number} rate
*/
minmaxDownSample(
valueDimension: DimensionLoose,
rate: number
): SeriesData<HostModel> {
const list = cloneListForMapAndSample(this);
list._store = this._store.minmaxDownSample(
this._getStoreDimIndex(valueDimension),
rate
);
return list as SeriesData<HostModel>;
}

/**
* Large data down sampling using largest-triangle-three-buckets
* @param {string} valueDimension
Expand Down
19 changes: 3 additions & 16 deletions src/processor/dataSample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,6 @@ const samplers: Dictionary<Sampler> = {
// NaN will cause illegal axis extent.
return isFinite(min) ? min : NaN;
},
minmax: function (frame) {
let turningPointAbsoluteValue = -Infinity;
let turningPointOriginalValue = -Infinity;

for (let i = 0; i < frame.length; i++) {
const originalValue = frame[i];
const absoluteValue = Math.abs(originalValue);

if (absoluteValue > turningPointAbsoluteValue) {
turningPointAbsoluteValue = absoluteValue;
turningPointOriginalValue = originalValue;
}
}

return isFinite(turningPointOriginalValue) ? turningPointOriginalValue : NaN;
},
// TODO
// Median
nearest: function (frame) {
Expand Down Expand Up @@ -115,6 +99,9 @@ export default function dataSample(seriesType: string): StageHandler {
if (sampling === 'lttb') {
seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate));
}
else if (sampling === 'minmax') {
seriesModel.setData(data.minmaxDownSample(data.mapDimension(valueAxis.dim), 1 / rate));
}
let sampler;
if (isString(sampling)) {
sampler = samplers[sampling];
Expand Down