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} >
diff --git a/src/components/explore/QuantileExplorerView.svelte b/src/components/explore/QuantileExplorerView.svelte index 027a500c1..ed2454d0e 100644 --- a/src/components/explore/QuantileExplorerView.svelte +++ b/src/components/explore/QuantileExplorerView.svelte @@ -47,7 +47,7 @@ let aggregationInfo; - let smoothnessLevel = false; // Only on/off now but intention is to be a gradient. + let interpolate = false; setContext('probeType', probeType); @@ -118,9 +118,8 @@ margin-bottom: 0px; } - .interpolator label { - color: var(--cool-gray-700); - margin-right: 5px; + .interpolator h3 { + padding-right: 5px; } @@ -170,7 +169,7 @@ {#each probeKeys as key, i (key)} {#each aggregationTypes as aggType, i (aggType + timeHorizon + key)} {#if key === currentKey && aggType === currentAggregation} - {#key smoothnessLevel} + {#key interpolate}
`${perc}%`} yScaleType={probeType === 'log' ? 'scalePoint' : 'linear'} {yDomain} - {smoothnessLevel} + {interpolate} >

- Interpolate + Interpolated