Skip to content

Commit

Permalink
Issue 49975: Lower and Upper bound lines are displayed incorrectly on…
Browse files Browse the repository at this point in the history
… qc plots (#5515)
  • Loading branch information
ankurjuneja authored May 22, 2024
1 parent ae5ecc0 commit c20b4c2
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 60 deletions.
201 changes: 141 additions & 60 deletions core/webapp/vis/src/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -1814,25 +1814,25 @@ boxPlot.render();
&& config.properties.valueConversion === 'percentDeviation') {
maxValue = mean * LABKEY.vis.Stat.MOVING_RANGE_UPPER_LIMIT_WEIGHT;
minValue = mean;
} else if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.LeveyJennings
&& config.properties.valueConversion === 'standardDeviation') {
} else if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.LeveyJennings) {
if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.StandardDeviation) {
maxValue = config.properties.upperBound + cushion;
minValue = config.properties.lowerBound - cushion;
}
else if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.MeanDeviation) {
maxValue = mean + config.properties.upperBound + cushion;
minValue = mean + config.properties.lowerBound - cushion;
if (config.properties.valueConversion === 'percentDeviation') {
// Multiplying by 10 to get the yAxisDomain[min & max] to be in the same range as the percent range
maxValue = mean + config.properties.upperBound * 10 + cushion;
minValue = mean + config.properties.lowerBound * 10 - cushion;
}
}
else {
maxValue = 3.2;
minValue = -3.2;
}
}
else if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.Absolute) {
maxValue = config.properties.upperBound + cushion;
minValue = config.properties.lowerBound - cushion;
}
else if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.MeanDeviation) {
maxValue = mean + config.properties.upperBound + cushion;
minValue = mean + config.properties.lowerBound - cushion;
}
else if (!config.properties.combined && stddev) {
maxValue = mean + ((config.properties.upperBound + cushion) * stddev);
minValue = mean + ((config.properties.lowerBound - cushion) * stddev);
Expand Down Expand Up @@ -1940,6 +1940,58 @@ boxPlot.render();

// Handle value conversions
convertValues(config.properties.valueConversion);
if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.LeveyJennings) {
meanStdDevData[index] = row;
if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.Absolute) {
row.upperBound = config.properties.upperBound;
row.lowerBound = config.properties.lowerBound;
}
else if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.MeanDeviation) {
row.upperBound = config.properties.upperBound + row[meanProp];
row.lowerBound = config.properties.lowerBound + row[meanProp];
}

if (config.properties.valueConversion === 'percentDeviation') {
row.upperBound = convertToPercentDeviation(row.upperBound, row[meanProp]);
row.lowerBound = convertToPercentDeviation(row.lowerBound, row[meanProp]);
if (config.legendData && config.legendData.length > 0) {
for (let i = 0; i < config.legendData.length; i++) {
let legendRow = config.legendData[i];
if (legendRow.text.indexOf("% of Mean") === -1) {
if (legendRow.text.indexOf("Upper") > -1) {
legendRow.text += " (" + row.upperBound + "% of Mean)";
} else if (legendRow.text.indexOf("Lower") > -1) {
legendRow.text += " (" + row.lowerBound + "% of Mean)";
}
}
}
}
}
else if (config.properties.valueConversion === 'standardDeviation') {
row.upperBound = convertToStandardDeviation(row.upperBound, row[meanProp], row[sdProp]);
row.lowerBound = convertToStandardDeviation(row.lowerBound, row[meanProp], row[sdProp]);
if (config.legendData && config.legendData.length > 0) {
for (let i = 0; i < config.legendData.length; i++) {
let legendRow = config.legendData[i];
if (legendRow.text.indexOf("SD") === -1) {
if (legendRow.text.indexOf("Upper") > -1) {
legendRow.text += " (" + row.upperBound + " SD)";
} else if (legendRow.text.indexOf("Lower") > -1) {
legendRow.text += " (" + row.lowerBound + " SD)";
}
}
}
}
}
if (config.properties.yAxisDomain) {
if (config.properties.yAxisDomain[0] > row.lowerBound) {
config.properties.yAxisDomain[0] = row.lowerBound - 0.2;
}
if (config.properties.yAxisDomain[1] < row.upperBound) {
config.properties.yAxisDomain[1] = row.upperBound + 0.2;
}
}
}
if (config.properties.valueConversion === 'percentDeviation') {
row[sdProp] = convertToPercentDeviation(row[sdProp], row[meanProp]);
row[meanProp] = 100;
Expand Down Expand Up @@ -2060,12 +2112,6 @@ boxPlot.render();

tickLabelMap[index] = row[config.properties.xTickLabel];
row.seqValue = index;

if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.LeveyJennings) {
meanStdDevData[index] = row;
row.upperBound = config.properties.upperBound;
row.lowerBound = config.properties.lowerBound;
}
}

// min x-axis tick length is 10 by default
Expand Down Expand Up @@ -2212,69 +2258,85 @@ boxPlot.render();
}
else {
var barWidth = Math.max(config.width / config.data[config.data.length-1].seqValue / 4, 3);

// the below if-else sections add the mean/SD/error bars to the plots
if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.LeveyJennings) {
config.layers = [];

if (config.properties.mean !== undefined) {
if (config.properties.stdDev !== undefined &&
config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.StandardDeviation &&
config.properties.showBoundLines) {
if (config.properties.stdDev !== undefined &&
config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.StandardDeviation &&
config.properties.showBoundLines) {

config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: LABKEY.vis.PlotProperties.Color.Outlier, dashed: true, width: barWidth, topOnly: true}),
data: meanStdDevData,
aes: {
error: function (row) {
return row[config.properties.stdDev] * config.properties.upperBound;
},
yLeft: config.properties.combined ? config.properties.stdDev : config.properties.mean
}
}));
config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: LABKEY.vis.PlotProperties.Color.Outlier, dashed: true, width: barWidth, topOnly: true}),
data: meanStdDevData,
aes: {
error: function (row) {
return row[config.properties.stdDev] * config.properties.lowerBound;
},
yLeft: config.properties.combined ? config.properties.stdDev : config.properties.mean
}
}));
if (config.properties.hideSDLines !== true) {
config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: 'red', dashed: true, width: barWidth, topOnly: true}),
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: LABKEY.vis.PlotProperties.TwoSD, dashed: true, width: barWidth}),
data: meanStdDevData,
aes: {
error: function (row) {
return row[config.properties.stdDev] * config.properties.upperBound;
},
error: function(row){return row[config.properties.stdDev] * 2;},
yLeft: config.properties.mean
}
}));

config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: 'red', dashed: true, width: barWidth, topOnly: true}),
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: LABKEY.vis.PlotProperties.Color.OneSD, dashed: true, width: barWidth}),
data: meanStdDevData,
aes: {
error: function (row) {
return row[config.properties.stdDev] * config.properties.lowerBound;
},
error: function(row){return row[config.properties.stdDev];},
yLeft: config.properties.mean
}
}));
if (config.properties.hideSDLines !== true) {
config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: 'blue', dashed: true, width: barWidth}),
data: meanStdDevData,
aes: {
error: function(row){return row[config.properties.stdDev] * 2;},
yLeft: config.properties.mean
}
}));

config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: 'green', dashed: true, width: barWidth}),
data: meanStdDevData,
aes: {
error: function(row){return row[config.properties.stdDev];},
yLeft: config.properties.mean
}
}));
}
}
}

// add the mean line
if (config.properties.mean !== undefined) {
config.layers.push(new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({size: 1, color: 'darkgrey', width: barWidth, topOnly: true}),
geom: new LABKEY.vis.Geom.ErrorBar({
size: 1,
color: LABKEY.vis.PlotProperties.Color.Mean,
width: barWidth,
topOnly: true
}),
data: meanStdDevData,
aes: {
error: function(row){return 0;},
error: function (row) {
return 0;
},
yLeft: config.properties.mean
}
}));
}

if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.Absolute) {
// add the upper and lower bound lines for absolute bound types
if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.Absolute && !config.properties.combined) {
if (config.properties.lowerBound) {
const lowerBoundLayer = new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({ size: 1, color: 'red', width: barWidth, dashed: true }),
geom: new LABKEY.vis.Geom.ErrorBar({
size: 1,
color: LABKEY.vis.PlotProperties.Color.Outlier,
width: barWidth,
dashed: true,
topOnly: true
}),
data: meanStdDevData,
aes: {
error: function (row) {
Expand All @@ -2287,7 +2349,13 @@ boxPlot.render();
}
if (config.properties.upperBound) {
const upperBoundLayer = new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({ size: 1, color: 'red', width: barWidth, dashed: true }),
geom: new LABKEY.vis.Geom.ErrorBar({
size: 1,
color: LABKEY.vis.PlotProperties.Color.Outlier,
width: barWidth,
dashed: true,
topOnly: true
}),
data: meanStdDevData,
aes: {
error: function (row) {
Expand All @@ -2300,14 +2368,21 @@ boxPlot.render();
}
}

if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.MeanDeviation) {
// add the upper and lower bound lines for mean deviation bound types
if (config.properties.boundType === LABKEY.vis.PlotProperties.BoundType.MeanDeviation && !config.properties.combined) {
if (config.properties.lowerBound) {
const lowerBoundLayer = new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({ size: 1, color: 'red', width: barWidth, dashed: true }),
geom: new LABKEY.vis.Geom.ErrorBar({
size: 1,
color: LABKEY.vis.PlotProperties.Color.Outlier,
width: barWidth,
dashed: true,
topOnly: true
}),
data: meanStdDevData,
aes: {
error: function (row) {
return row[config.properties.mean];
return 0;
},
yLeft: 'lowerBound'
}
Expand All @@ -2316,11 +2391,17 @@ boxPlot.render();
}
if (config.properties.upperBound) {
const upperBoundLayer = new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ErrorBar({ size: 1, color: 'red', width: barWidth, dashed: true }),
geom: new LABKEY.vis.Geom.ErrorBar({
size: 1,
color: LABKEY.vis.PlotProperties.Color.Outlier,
width: barWidth,
dashed: true,
topOnly: true
}),
data: meanStdDevData,
aes: {
error: function (row) {
return row[config.properties.mean];
return 0;
},
yLeft: 'upperBound'
}
Expand All @@ -2331,7 +2412,7 @@ boxPlot.render();
}
else if (config.qcPlotType === LABKEY.vis.TrendingLinePlotType.CUSUM) {
var range = new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ControlRange({size: 1, color: 'red', dashed: true, width: barWidth}),
geom: new LABKEY.vis.Geom.ControlRange({size: 1, color: LABKEY.vis.PlotProperties.Color.Outlier, dashed: true, width: barWidth}),
data: config.data,
aes: {
upper: function(){return LABKEY.vis.Stat.CUSUM_CONTROL_LIMIT;},
Expand All @@ -2347,7 +2428,7 @@ boxPlot.render();
}
else {
var range = new LABKEY.vis.Layer({
geom: new LABKEY.vis.Geom.ControlRange({size: 1, color: 'red', dashed: true, width: barWidth}),
geom: new LABKEY.vis.Geom.ControlRange({size: 1, color: LABKEY.vis.PlotProperties.Color.Outlier, dashed: true, width: barWidth}),
data: config.data,
aes: {
upper: function(row){return row[config.properties.meanMR] * LABKEY.vis.Stat.MOVING_RANGE_UPPER_LIMIT_WEIGHT;},
Expand Down
8 changes: 8 additions & 0 deletions core/webapp/vis/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ if (!LABKEY.vis.PlotProperties) {
StandardDeviation: 'stddev'
}
}
if (!LABKEY.vis.PlotProperties.Color) {
LABKEY.vis.PlotProperties.Color = {
Outlier: 'red',
Mean: 'darkgrey',
OneSD: 'green',
TwoSD: 'blue',
}
}
}

LABKEY.vis.makeLine = function(x1, y1, x2, y2){
Expand Down

0 comments on commit c20b4c2

Please sign in to comment.