From 9825f187b6c90972f6bf1b670884088b5178179e Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Sat, 3 Jan 2026 08:49:41 -0800 Subject: [PATCH 1/2] QC plot y-scale option to show delta from mean --- core/webapp/vis/src/plot.js | 30 ++++++++++++++++++++++++++---- core/webapp/vis/src/utils.js | 3 ++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/core/webapp/vis/src/plot.js b/core/webapp/vis/src/plot.js index 2599582bb42..363e05d1203 100644 --- a/core/webapp/vis/src/plot.js +++ b/core/webapp/vis/src/plot.js @@ -1612,7 +1612,7 @@ boxPlot.render(); * @param {Number} [config.properties.boundType] Either 'absolute' or 'stddev', to describe how to interpret the upperBound and lowerBound. * Used by LeveyJennings only. Defaults to 'stddev'. * @param {String} [config.properties.valueConversion] The data property name for the conversion of the plot to either percent - * of the mean ('percentDeviation') or standard deviations ('standardDeviation'). + * of the mean ('percentDeviation'), standard deviations ('standardDeviation') or delta from mean ('deltaFromMean'). * Used by LeveyJennings and Moving Range only. * @param {String} [config.properties.defaultGuideSets] The data property name for default std dev and mean needed for percentDeviation * or standardDeviation conversion. @@ -1784,7 +1784,7 @@ boxPlot.render(); config.properties.TrailingMeanRight || config.properties.TrailingCVRight; - var convertToPercentDeviation = function(value, mean) { + let convertToPercentDeviation = function(value, mean) { var calc = Math.round(((value / mean) * 100) * 100) / 100; if (isNaN(calc)) return 100; @@ -1792,7 +1792,7 @@ boxPlot.render(); return calc; }; - var convertToStandardDeviation = function(value, mean, stddev) { + let convertToStandardDeviation = function(value, mean, stddev) { var calc = Math.round(((value - mean) / stddev) * 100) / 100; if (isNaN(calc)) return 0; @@ -1800,6 +1800,10 @@ boxPlot.render(); return calc; }; + let convertToDeltaFromMean = function(value, mean) { + return (value - mean); + }; + var convertValues = function(conversion) { if (!conversion) return; @@ -1813,6 +1817,9 @@ boxPlot.render(); if (conversion === LABKEY.vis.PlotProperties.ValueConversion.PercentDeviation) { row[valProp] = convertToPercentDeviation(row[valProp], row[meanProp]); } + if (conversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + row[valProp] = convertToDeltaFromMean(row[valProp], row[meanProp]); + } else { row[valProp] = convertToStandardDeviation(row[valProp], row[meanProp], row[sdProp]); } @@ -1822,6 +1829,9 @@ boxPlot.render(); if (conversion === LABKEY.vis.PlotProperties.ValueConversion.PercentDeviation) { row[valRightProp] = convertToPercentDeviation(row[valRightProp], row[meanProp]); } + if (conversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + row[valRightProp] = convertToDeltaFromMean(row[valRightProp], row[meanProp]); + } else { row[valRightProp] = convertToStandardDeviation(row[valRightProp], row[meanProp], row[sdProp]); } @@ -1843,6 +1853,10 @@ boxPlot.render(); maxValue = config.properties.upperBound + cushion; minValue = config.properties.lowerBound - cushion; } + else if (config.properties.valueConversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + maxValue = config.properties.upperBound + mean + cushion; + minValue = config.properties.lowerBound - mean - cushion; + } else { maxValue = mean + ((config.properties.upperBound + cushion) * stddev); minValue = mean + ((config.properties.lowerBound - cushion) * stddev); @@ -1981,7 +1995,6 @@ boxPlot.render(); // Handle value conversions convertValues(config.properties.valueConversion); if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.LeveyJennings) { - if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.Absolute) { row.upperBound = config.properties.upperBound; row.lowerBound = config.properties.lowerBound; @@ -2028,6 +2041,11 @@ boxPlot.render(); } } + else if (config.properties.valueConversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + row.upperBound = convertToDeltaFromMean(row.upperBound, row[meanProp]); + row.lowerBound = convertToDeltaFromMean(row.lowerBound, row[meanProp]); + } + if (config.properties.yAxisDomain) { if (config.properties.yAxisDomain[0] > row.lowerBound) { config.properties.yAxisDomain[0] = row.lowerBound - cushion; @@ -2045,6 +2063,10 @@ boxPlot.render(); row[sdProp] = 1; row[meanProp] = 0; } + else if (config.properties.valueConversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + row[sdProp] = 0; + row[meanProp] = 0; + } if (!config.properties.valueRight && !config.properties.valueRightMR && diff --git a/core/webapp/vis/src/utils.js b/core/webapp/vis/src/utils.js index 9dfbf0ab2c1..7eec6ca276f 100644 --- a/core/webapp/vis/src/utils.js +++ b/core/webapp/vis/src/utils.js @@ -40,7 +40,8 @@ if (!LABKEY.vis.PlotProperties) { if (!LABKEY.vis.PlotProperties.ValueConversion) { LABKEY.vis.PlotProperties.ValueConversion = { StandardDeviation: 'standardDeviation', - PercentDeviation: 'percentDeviation' + PercentDeviation: 'percentDeviation', + DeltaFromMean: 'deltaFromMean' } } } From c0a07e812ef726a08853aa0a9c158b99b166267e Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Mon, 12 Jan 2026 16:32:34 -0800 Subject: [PATCH 2/2] fix conditions --- core/webapp/vis/src/plot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/webapp/vis/src/plot.js b/core/webapp/vis/src/plot.js index 363e05d1203..8210eba853e 100644 --- a/core/webapp/vis/src/plot.js +++ b/core/webapp/vis/src/plot.js @@ -1817,7 +1817,7 @@ boxPlot.render(); if (conversion === LABKEY.vis.PlotProperties.ValueConversion.PercentDeviation) { row[valProp] = convertToPercentDeviation(row[valProp], row[meanProp]); } - if (conversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + else if (conversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { row[valProp] = convertToDeltaFromMean(row[valProp], row[meanProp]); } else { @@ -1829,7 +1829,7 @@ boxPlot.render(); if (conversion === LABKEY.vis.PlotProperties.ValueConversion.PercentDeviation) { row[valRightProp] = convertToPercentDeviation(row[valRightProp], row[meanProp]); } - if (conversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { + else if (conversion === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean) { row[valRightProp] = convertToDeltaFromMean(row[valRightProp], row[meanProp]); } else {