diff --git a/capabilities.json b/capabilities.json index 277aeee..c302be4 100644 --- a/capabilities.json +++ b/capabilities.json @@ -36,18 +36,19 @@ "displayName": "Chart Type", "type" : { "enumeration" : [ - { "displayName" : "run - Run Chart", "value" : "run" }, - { "displayName" : "i - Individual Measurements", "value" : "i" }, - { "displayName" : "mr - Moving Range of Individual Measurements", "value" : "mr" }, - { "displayName" : "p - Proportions", "value" : "p" }, - { "displayName" : "p prime - Proportions: Large-Sample Corrected", "value" : "pp" }, - { "displayName" : "u - Rates", "value" : "u" }, - { "displayName" : "u prime - Rates: Large-Sample Correction", "value" : "up" }, - { "displayName" : "c - Counts", "value" : "c" }, - { "displayName" : "xbar - Sample Means", "value" : "xbar" }, - { "displayName" : "s - Sample SDs", "value" : "s" }, - { "displayName" : "g - Number of Non-Events Between Events", "value" : "g" }, - { "displayName" : "t - Time Between Events", "value" : "t" } + { "displayName" : "run - Run Chart", "value" : "run" }, + { "displayName" : "i - Individual Measurements", "value" : "i" }, + { "displayName" : "i_m - Individual Measurements (Median centerline)", "value" : "i_m" }, + { "displayName" : "mr - Moving Range of Individual Measurements", "value" : "mr" }, + { "displayName" : "p - Proportions", "value" : "p" }, + { "displayName" : "p prime - Proportions: Large-Sample Corrected", "value" : "pp" }, + { "displayName" : "u - Rates", "value" : "u" }, + { "displayName" : "u prime - Rates: Large-Sample Correction", "value" : "up" }, + { "displayName" : "c - Counts", "value" : "c" }, + { "displayName" : "xbar - Sample Means", "value" : "xbar" }, + { "displayName" : "s - Sample SDs", "value" : "s" }, + { "displayName" : "g - Number of Non-Events Between Events", "value" : "g" }, + { "displayName" : "t - Time Between Events", "value" : "t" } ] } }, diff --git a/src/Classes/derivedSettingsClass.ts b/src/Classes/derivedSettingsClass.ts index 8e40ec1..e00209d 100644 --- a/src/Classes/derivedSettingsClass.ts +++ b/src/Classes/derivedSettingsClass.ts @@ -1,4 +1,20 @@ -import {defaultSettingsType} from "./settingsClass" +import { defaultSettingsType } from "./settingsClass" + +const valueNames: Record = { + "i": "Observation", + "i_m": "Observation", + "c": "Count", + "t": "Time", + "xbar": "Group Mean", + "s": "Group SD", + "g": "Non-Events", + "run": "Observation", + "mr": "Moving Range", + "p": "Proportion", + "pp": "Proportion", + "u": "Rate", + "up": "Rate" +} export default class derivedSettingsClass { multiplier: number @@ -9,7 +25,9 @@ export default class derivedSettingsClass { numerator_non_negative: boolean, numerator_leq_denominator: boolean, has_control_limits: boolean, - needs_sd: boolean + needs_sd: boolean, + integer_num_den: boolean, + value_name: string } update(inputSettings: defaultSettingsType) { @@ -35,11 +53,13 @@ export default class derivedSettingsClass { this.chart_type_props = { needs_denominator: ["p", "pp", "u", "up", "xbar", "s"].includes(chartType), - denominator_optional: ["i", "run", "mr"].includes(chartType), + denominator_optional: ["i", "i_m", "run", "mr"].includes(chartType), numerator_non_negative: ["p", "pp", "u", "up", "s", "c", "g", "t"].includes(chartType), numerator_leq_denominator: ["p", "pp", "u", "up"].includes(chartType), has_control_limits: !(["run"].includes(chartType)), - needs_sd: ["xbar"].includes(chartType) + needs_sd: ["xbar"].includes(chartType), + integer_num_den: ["c", "p", "pp"].includes(chartType), + value_name: valueNames[chartType] } this.multiplier = multiplier diff --git a/src/Classes/viewModelClass.ts b/src/Classes/viewModelClass.ts index ddb90cc..3067fbb 100644 --- a/src/Classes/viewModelClass.ts +++ b/src/Classes/viewModelClass.ts @@ -214,7 +214,8 @@ export default class viewModelClass { this.inputData.limitInputArgs.keys[i].id) .createSelectionId(), highlighted: this.inputData.highlights?.[index] != null, - tooltip: buildTooltip(i, this.controlLimits, this.outliers, this.inputData, this.inputSettings.settings, this.inputSettings.derivedSettings) + tooltip: buildTooltip(i, this.controlLimits, this.outliers, this.inputData, + this.inputSettings.settings, this.inputSettings.derivedSettings) }) this.tickLabels.push({x: index, label: this.controlLimits.keys[i].label}); } diff --git a/src/Functions/buildTooltip.ts b/src/Functions/buildTooltip.ts index 5b0791d..31b35fa 100644 --- a/src/Functions/buildTooltip.ts +++ b/src/Functions/buildTooltip.ts @@ -3,23 +3,6 @@ type VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem; import type { controlLimitsObject, defaultSettingsType, derivedSettingsClass, outliersObject } from "../Classes"; import type { dataObject } from "./extractInputData"; -const valueNames: Record = { - "i": "Observation", - "c": "Count", - "t": "Time", - "xbar": "Group Mean", - "s": "Group SD", - "g": "Non-Events", - "run": "Observation", - "mr": "Moving Range", - "p": "Proportion", - "pp": "Proportion", - "u": "Rate", - "up": "Rate" -} - -const integerParams: string[] = ["c", "p", "pp"]; - /** * Builds the tooltip data for a specific index in the chart. * @@ -39,7 +22,6 @@ export default function buildTooltip(index: number, inputData: dataObject, inputSettings: defaultSettingsType, derivedSettings: derivedSettingsClass): VisualTooltipDataItem[] { - const chart_type: string = inputSettings.spc.chart_type; const numerator: number = controlLimits.numerators?.[index]; const denominator: number = controlLimits.denominators?.[index]; const target: number = controlLimits.targets[index]; @@ -61,7 +43,6 @@ export default function buildTooltip(index: number, const ast_limit: string = inputSettings.outliers.astronomical_limit; const two_in_three_limit: string = inputSettings.outliers.two_in_three_limit; const suffix: string = derivedSettings.percentLabels ? "%" : ""; - const intNumDen: boolean = integerParams.includes(chart_type); const sig_figs: number = inputSettings.spc.sig_figs; const tooltip: VisualTooltipDataItem[] = new Array(); @@ -72,20 +53,20 @@ export default function buildTooltip(index: number, if (inputSettings.spc.ttip_show_value) { const ttip_label_value: string = inputSettings.spc.ttip_label_value; tooltip.push({ - displayName: ttip_label_value === "Automatic" ? valueNames[chart_type] : ttip_label_value, + displayName: ttip_label_value === "Automatic" ? derivedSettings.chart_type_props.value_name : ttip_label_value, value: (controlLimits.values[index]).toFixed(sig_figs) + suffix }) } if(inputSettings.spc.ttip_show_numerator && !(numerator === null || numerator === undefined)) { tooltip.push({ displayName: inputSettings.spc.ttip_label_numerator, - value: (numerator).toFixed(intNumDen ? 0 : sig_figs) + value: (numerator).toFixed(derivedSettings.chart_type_props.integer_num_den ? 0 : sig_figs) }) } if(inputSettings.spc.ttip_show_denominator && !(denominator === null || denominator === undefined)) { tooltip.push({ displayName: inputSettings.spc.ttip_label_denominator, - value: (denominator).toFixed(intNumDen ? 0 : sig_figs) + value: (denominator).toFixed(derivedSettings.chart_type_props.integer_num_den ? 0 : sig_figs) }) } if (inputSettings.lines.show_specification && inputSettings.lines.ttip_show_specification) { diff --git a/src/Limit Calculations/i_m.ts b/src/Limit Calculations/i_m.ts new file mode 100644 index 0000000..142da9a --- /dev/null +++ b/src/Limit Calculations/i_m.ts @@ -0,0 +1,32 @@ +import { abs, diff, divide, rep, median } from "../Functions"; +import { type controlLimitsObject, type controlLimitsArgs } from "../Classes"; + +export default function imLimits(args: controlLimitsArgs): controlLimitsObject { + const useRatio: boolean = (args.denominators && args.denominators.length > 0); + const ratio: number[] = useRatio + ? divide(args.numerators, args.denominators) + : args.numerators; + + const cl: number = median(ratio); + + const consec_diff: number[] = abs(diff(ratio)); + const consec_diff_ulim: number = median(consec_diff) * 3.267; + const outliers_in_limits: boolean = args.outliers_in_limits; + const consec_diff_valid: number[] = outliers_in_limits ? consec_diff : consec_diff.filter(d => d < consec_diff_ulim); + + const sigma: number = median(consec_diff_valid) / 1.128; + + return { + keys: args.keys, + values: ratio.map(d => isNaN(d) ? 0 : d), + numerators: useRatio ? args.numerators : undefined, + denominators: useRatio ? args.denominators : undefined, + targets: rep(cl, args.keys.length), + ll99: rep(cl - 3 * sigma, args.keys.length), + ll95: rep(cl - 2 * sigma, args.keys.length), + ll68: rep(cl - 1 * sigma, args.keys.length), + ul68: rep(cl + 1 * sigma, args.keys.length), + ul95: rep(cl + 2 * sigma, args.keys.length), + ul99: rep(cl + 3 * sigma, args.keys.length) + }; +} diff --git a/src/Limit Calculations/index.ts b/src/Limit Calculations/index.ts index 1355a82..1323443 100644 --- a/src/Limit Calculations/index.ts +++ b/src/Limit Calculations/index.ts @@ -1,6 +1,7 @@ export { default as c } from "./c" export { default as g } from "./g" export { default as i } from "./i" +export { default as i_m } from "./i_m" export { default as mr } from "./mr" export { default as p } from "./p" export { default as pp } from "./pprime" @@ -11,4 +12,3 @@ export { default as u } from "./u" export { default as up } from "./uprime" export { default as xbar } from "./xbar" export { default as run } from "./run" - diff --git a/src/defaultSettings.ts b/src/defaultSettings.ts index 57b73b8..527fde0 100644 --- a/src/defaultSettings.ts +++ b/src/defaultSettings.ts @@ -42,7 +42,7 @@ const defaultSettings = { right_padding: { default: 10 } }, spc: { - chart_type: { default: "i", valid: ["run", "i", "mr", "p", "pp", "u", "up", "c", "xbar", "s", "g", "t"] }, + chart_type: { default: "i", valid: ["run", "i", "i_m", "mr", "p", "pp", "u", "up", "c", "xbar", "s", "g", "t"] }, outliers_in_limits: { default: false }, multiplier: { default: 1, valid: { numberRange: { min: 0 } } }, sig_figs: { default: 2, valid: { numberRange: { min: 0, max: 20 } } },