Skip to content

Commit

Permalink
Add directional flagging, change default target type
Browse files Browse the repository at this point in the history
  • Loading branch information
andrjohns committed Oct 30, 2023
1 parent 49053df commit 29899bd
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 67 deletions.
52 changes: 43 additions & 9 deletions capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,23 @@
"outliers" : {
"displayName": "Outlier Highlighting",
"properties": {
"flag_direction": {
"displayName": "Direction to Flag",
"process_flag_type": {
"displayName": "Type of Change to Flag",
"type": {
"enumeration" : [
{ "displayName" : "Both", "value" : "both" },
{ "displayName" : "Upper", "value" : "upper" },
{ "displayName" : "Lower", "value" : "lower" }
{ "displayName" : "Both", "value" : "both" },
{ "displayName" : "Improvement (Imp.)", "value" : "improvement" },
{ "displayName" : "Deterioration (Det.)", "value" : "deterioration" }
]
}
},
"improvement_direction": {
"displayName": "Improvement Direction",
"type": {
"enumeration" : [
{ "displayName" : "Increase", "value" : "increase" },
{ "displayName" : "Neutral", "value" : "neutral" },
{ "displayName" : "Decrease", "value" : "decrease" }
]
}
},
Expand All @@ -86,12 +96,36 @@
"displayName": "Two Sigma Outliers",
"type" : { "bool" : true }
},
"three_sigma_colour":{
"displayName": "Three Sigma Colour",
"three_sigma_colour_improvement":{
"displayName": "Imp. Three Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"three_sigma_colour_deterioration":{
"displayName": "Det. Three Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"three_sigma_colour_neutral_low":{
"displayName": "Neutral (Low) Three Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"three_sigma_colour_neutral_high":{
"displayName": "Neutral (High) Three Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"two_sigma_colour_improvement":{
"displayName": "Imp. Two Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"two_sigma_colour_deterioration":{
"displayName": "Det. Two Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"two_sigma_colour_neutral_low":{
"displayName": "Neutral (Low) Two Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
},
"two_sigma_colour":{
"displayName": "Two Sigma Colour",
"two_sigma_colour_neutral_high":{
"displayName": "Neutral (High) Two Sigma Colour",
"type": { "fill": { "solid": { "color": true } } }
}
}
Expand Down
2 changes: 1 addition & 1 deletion pbiviz.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"displayName":"Funnel Plots",
"guid":"PBIFUN",
"visualClassName":"Visual",
"version":"1.5.0.2",
"version":"1.5.0.3",
"description":"A PowerBI custom visual for funnel plots",
"supportUrl":"https://github.com/AUS-DOH-Safety-and-Quality/PowerBI-Funnels",
"gitHubUrl":"https://github.com/AUS-DOH-Safety-and-Quality/PowerBI-Funnels"
Expand Down
27 changes: 15 additions & 12 deletions src/Classes/viewModelClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type IVisualHost = powerbi.extensibility.visual.IVisualHost;
type VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem;
type ISelectionId = powerbi.visuals.ISelectionId;
import { chartClass, settingsClass, type limitData, plotPropertiesClass, type defaultSettingsType } from "../Classes"
import { extractInputData, buildTooltip, type dataObject } from "../Functions";
import { extractInputData, buildTooltip, type dataObject, checkFlagDirection } from "../Functions";
import * as chartObjects from "../Chart Types"
import getTransformation from "../Funnel Calculations/getTransformation";
import two_sigma from "../Outlier Flagging/two_sigma"
Expand Down Expand Up @@ -83,9 +83,6 @@ export default class viewModelClass {
const data_type: string = this.inputSettings.settings.funnel.chart_type;
const flag_two_sigma: boolean = this.inputSettings.settings.outliers.two_sigma;
const flag_three_sigma: boolean = this.inputSettings.settings.outliers.three_sigma;
const flag_direction: string = this.inputSettings.settings.outliers.flag_direction;
const two_sigma_colour: string = this.inputSettings.settings.outliers.two_sigma_colour;
const three_sigma_colour: string = this.inputSettings.settings.outliers.three_sigma_colour;

for (let i: number = 0; i < this.inputData.id.length; i++) {
const original_index: number = this.inputData.id[i];
Expand All @@ -95,17 +92,23 @@ export default class viewModelClass {
const limits_impl: limitData[] = this.calculatedLimits.filter(d => d.denominators === denominator && d.ll99 !== null && d.ul99 !== null);
const limits: limitData = limits_impl.length > 0 ? limits_impl[0] : this.calculatedLimits.filter(d => d.denominators === denominator)[0];
const aesthetics: defaultSettingsType["scatter"] = this.inputData.scatter_formatting[i]
const two_sigma_outlier: boolean = flag_two_sigma ? two_sigma(ratio, flag_direction, limits) : false;
const three_sigma_outlier: boolean = flag_three_sigma ? three_sigma(ratio, flag_direction, limits) : false;
const two_sigma_outlier: string = flag_two_sigma ? two_sigma(ratio, limits) : "none";
const three_sigma_outlier: string = flag_three_sigma ? three_sigma(ratio, limits) : "none";
const category: string = (typeof this.inputData.categories.values[original_index] === "number") ?
(this.inputData.categories.values[original_index]).toString() :
<string>(this.inputData.categories.values[original_index]);
if (two_sigma_outlier) {
aesthetics.colour = two_sigma_colour;
const flagSettings = {
process_flag_type: this.inputSettings.settings.outliers.process_flag_type,
improvement_direction: this.inputSettings.settings.outliers.improvement_direction
}
if (two_sigma_outlier !== "none") {
const two_sigma_flag: string = checkFlagDirection(two_sigma_outlier, flagSettings)
aesthetics.colour = this.inputSettings.settings.outliers["two_sigma_colour_" + two_sigma_flag];
}

if (three_sigma_outlier) {
aesthetics.colour = three_sigma_colour
if (three_sigma_outlier !== "none") {
const three_sigma_flag: string = checkFlagDirection(three_sigma_outlier, flagSettings)
aesthetics.colour = this.inputSettings.settings.outliers["three_sigma_colour_" + three_sigma_flag];
}

plotPoints.push({
Expand All @@ -126,8 +129,8 @@ export default class viewModelClass {
limits: limits,
data_type: data_type,
multiplier: multiplier,
two_sigma_outlier: two_sigma_outlier,
three_sigma_outlier: three_sigma_outlier,
two_sigma_outlier: two_sigma_outlier !== "none",
three_sigma_outlier: three_sigma_outlier !== "none",
sig_figs: this.inputSettings.settings.funnel.sig_figs,
userTooltips: this.inputData.tooltips[i]
})
Expand Down
33 changes: 33 additions & 0 deletions src/Functions/checkFlagDirection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export default function checkFlagDirection(outlierStatus: string,
flagSettings: { process_flag_type: string,
improvement_direction: string}): string {
if (outlierStatus === "none") {
return outlierStatus;
}

const increaseDirectionMap: Record<string, string> = {
"upper" : "improvement",
"lower" : "deterioration"
}
const decreaseDirectionMap: Record<string, string> = {
"lower" : "improvement",
"upper" : "deterioration"
}
const neutralDirectionMap: Record<string, string> = {
"lower" : "neutral_low",
"upper" : "neutral_high"
}
const flagDirectionMap: Record<string, string> = {
"increase" : increaseDirectionMap[outlierStatus],
"decrease" : decreaseDirectionMap[outlierStatus],
"neutral" : neutralDirectionMap[outlierStatus]
}

const mappedFlag: string = flagDirectionMap[flagSettings.improvement_direction];

if (flagSettings.process_flag_type !== "both") {
return mappedFlag === flagSettings.process_flag_type ? mappedFlag : "none";
} else {
return mappedFlag;
}
}
1 change: 1 addition & 0 deletions src/Functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export { mean, median, sum, max, min, quantile } from "d3-array";
export { default as validateDataView } from "./validateDataView"
export { default as validateInputData } from "./validateInputData"
export { default as formatPrimitiveValue } from "./formatPrimitiveValue"
export { default as checkFlagDirection } from "./checkFlagDirection"
28 changes: 8 additions & 20 deletions src/Outlier Flagging/three_sigma.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import type { limitData } from "../Classes";
import { between } from "../Functions"

export default function three_sigma(value: number, flag_direction: string,
limits: limitData): boolean {
if (limits.ul99 === null && limits.ll99 === null) {
return false;
} else if (limits.ul99 === null) {
flag_direction = "lower"
} else if (limits.ll99 === null) {
flag_direction = "upper"
export default function three_sigma(value: number,
limits: limitData): string {
if (value < limits.ll99) {
return "lower";
} else if (value > limits.ul99) {
return "upper";
} else {
return "none";
}

if (!between(value, limits.ll99, limits.ul99)) {
if (flag_direction === "both") {
return true;
} else if (flag_direction == "upper") {
return value > limits.ul99;
} else if (flag_direction == "lower") {
return value < limits.ll99;
}
}
return false;
}
28 changes: 8 additions & 20 deletions src/Outlier Flagging/two_sigma.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import type { limitData } from "../Classes";
import { between } from "../Functions"

export default function two_sigma(value: number, flag_direction: string,
limits: limitData): boolean {
if (limits.ul95 === null && limits.ll95 === null) {
return false;
} else if (limits.ul95 === null) {
flag_direction = "lower"
} else if (limits.ll95 === null) {
flag_direction = "upper"
export default function two_sigma(value: number,
limits: limitData): string {
if (value < limits.ll99) {
return "lower";
} else if (value > limits.ul99) {
return "upper";
} else {
return "none";
}

if (!between(value, limits.ll95, limits.ul95)) {
if (flag_direction === "both") {
return true;
} else if (flag_direction === "upper") {
return value > limits.ul95;
} else if (flag_direction === "lower") {
return value < limits.ll95;
}
}
return false;
}
17 changes: 12 additions & 5 deletions src/defaultSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const defaultSettings = {
width_alt_target: 1.5,
type_99: "10 10",
type_95: "2 5",
type_target: "10 10",
type_alt_target: "10 10",
type_target: "10 0",
type_alt_target: "10 0",
colour_99: "#6495ED",
colour_95: "#6495ED",
colour_target: "#000000",
Expand Down Expand Up @@ -65,11 +65,18 @@ const defaultSettings = {
ylimit_u: <number>null
},
outliers: {
flag_direction: "both",
process_flag_type: "both",
improvement_direction: "increase",
three_sigma: false,
three_sigma_colour: "#E1C233",
three_sigma_colour_improvement: "#E1C233",
three_sigma_colour_deterioration: "#E1C233",
three_sigma_colour_neutral_low: "#E1C233",
three_sigma_colour_neutral_high: "#E1C233",
two_sigma: false,
two_sigma_colour: "#E1C233"
two_sigma_colour_improvement: "#E1C233",
two_sigma_colour_deterioration: "#E1C233",
two_sigma_colour_neutral_low: "#E1C233",
two_sigma_colour_neutral_high: "#E1C233"
}
}

Expand Down

0 comments on commit 29899bd

Please sign in to comment.