diff --git a/src/components/explore/ProbeExplorer.svelte b/src/components/explore/ProbeExplorer.svelte
index 66fa885ab..eda5b739e 100644
--- a/src/components/explore/ProbeExplorer.svelte
+++ b/src/components/explore/ProbeExplorer.svelte
@@ -42,7 +42,7 @@
export let normalizedData;
- export let smoothnessLevel;
+ export let interpolate;
export let pointMetricType;
export let overTimePointMetricType = pointMetricType;
@@ -56,44 +56,92 @@
? normData.filter((d) => !isEmpty(d.non_norm_histogram))
: normData;
- function smoothenData(data, accessor, level) {
- // Interpolates percentiles values by applying a Moving Average.
- if (!level) {
- return data;
+ function getInterpPercBtnRanksForHistogram(histogram, percentiles) {
+ // Compute cumulative frequencies
+ let cumFreq = [];
+ let totalFreq = 0;
+
+ for (let i = 0; i < histogram.length; i += 1) {
+ totalFreq += histogram[i].value;
+ cumFreq.push({ bin: histogram[i].bin, cumFreq: totalFreq });
+ }
+ // Normalize so we can find percentiles appropriately
+ if (totalFreq !== 1) {
+ cumFreq = cumFreq.map((item) => ({
+ bin: item.bin,
+ cumFreq: item.cumFreq / totalFreq,
+ }));
}
- const windowSize = data.length / 100;
- const dataField = data[0][accessor];
- const keys = Object.keys(dataField);
- return data.map((item, idx) => {
- const windowData = data.slice(Math.max(0, idx - windowSize + 1), idx + 1);
+ // Find the interval where each percentile falls and interpolate
+ const percentileValues = {};
+ for (let i = 0; i < percentiles.length; i += 1) {
+ const percentile = percentiles[i];
+ let targetFreq = percentile / 100;
+ if (targetFreq <= cumFreq[0].cumFreq) {
+ percentileValues[percentile] = cumFreq[0].bin;
+ }
+ if (targetFreq >= totalFreq) {
+ percentileValues[percentile] = cumFreq[cumFreq.length - 1].bin;
+ }
+ for (let j = 0; j < cumFreq.length - 1; j += 1) {
+ if (
+ cumFreq[j].cumFreq <= targetFreq &&
+ cumFreq[j + 1].cumFreq >= targetFreq
+ ) {
+ let x0 = cumFreq[j].cumFreq;
+ let x1 = cumFreq[j + 1].cumFreq;
+ let y0 = cumFreq[j].bin;
+ let y1 = cumFreq[j + 1].bin;
+ // Linear interpolation formula
+ let percentileValue =
+ y0 + ((targetFreq - x0) * (y1 - y0)) / (x1 - x0);
+ percentileValues[percentile] = percentileValue;
+ }
+ }
+ }
+ return percentileValues;
+ }
- const smoothedValues = keys.reduce((acc, key) => {
- const sum = windowData.reduce(
- (total, wItem) => total + wItem[accessor][key],
- 0
- );
- acc[key] = sum / windowData.length;
- return acc;
- }, {});
- const { [accessor]: _, ...rest } = item;
+ function getInterpolatedPercentilesBtnRanks(
+ data,
+ percentileAccessor,
+ normalizationType
+ ) {
+ // Generates percentiles using the Interpolation Between Closest Ranks method.
+ const histogramAccessor =
+ normalizationType === 'normalized' ? 'histogram' : 'non_norm_histogram';
+ const percentiles = Object.keys(data[0][percentileAccessor]);
+
+ return data.map((item) => {
+ const histogram = item[histogramAccessor];
+ const interpVals = histogram
+ ? getInterpPercBtnRanksForHistogram(histogram, percentiles)
+ : percentiles;
+ const { [percentileAccessor]: _, ...rest } = item;
return {
...rest,
- [accessor]: smoothedValues,
+ [percentileAccessor]: interpVals,
};
});
}
- function filterAndSmoothenData(data, normalizationType) {
+ function filterAndInterpolateData(data, normalizationType) {
const filtered = filterData(data, normalizationType);
- return smoothenData(filtered, overTimePointMetricType, smoothnessLevel);
+ return interpolate
+ ? getInterpolatedPercentilesBtnRanks(
+ filtered,
+ overTimePointMetricType,
+ normalizationType
+ )
+ : filtered;
}
- let data = filterAndSmoothenData(
+ let data = filterAndInterpolateData(
normalizedData,
$store.productDimensions.normalizationType
);
- $: data = filterAndSmoothenData(
+ $: data = filterAndInterpolateData(
normalizedData,
$store.productDimensions.normalizationType
);
@@ -445,7 +493,6 @@
}
}}
{distViewButtonId}
- {smoothnessLevel}
>