add 90th percentile amount, range, interquartile range, variance, and standard deviation to the value metrics in insights explorer

This commit is contained in:
MaysWind
2026-03-07 22:18:33 +08:00
parent 8e5202b375
commit d517a1862b
6 changed files with 55 additions and 15 deletions
+1 -1
View File
@@ -81,7 +81,7 @@ export function usePieChartBase(props: CommonPieChartProps) {
accumulatedPaintPercent += finalItem.paintPercent; accumulatedPaintPercent += finalItem.paintPercent;
finalItem.displayPercent = formatPercentToLocalizedNumerals(finalItem.percent, 2, '<0.01'); finalItem.displayPercent = formatPercentToLocalizedNumerals(finalItem.percent, 2, '<0.01');
finalItem.displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : formatNumberToLocalizedNumerals(value); finalItem.displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : formatNumberToLocalizedNumerals(value, 2);
validItems.push(finalItem); validItems.push(finalItem);
} }
+1 -1
View File
@@ -405,7 +405,7 @@ function getDisplayValue(value: number): string {
return formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency); return formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency);
} }
return formatNumberToLocalizedNumerals(value); return formatNumberToLocalizedNumerals(value, 2);
} }
function clickItem(e: ECElementEvent): void { function clickItem(e: ECElementEvent): void {
+1 -1
View File
@@ -84,7 +84,7 @@ const radarData = computed<RadarChartData>(() => {
const finalPercent = (isNumber(percent) && percent >= 0) ? percent : (value > 0 ? value / totalValidValue * 100 : 0); const finalPercent = (isNumber(percent) && percent >= 0) ? percent : (value > 0 ? value / totalValidValue * 100 : 0);
const displayPercent = formatPercentToLocalizedNumerals(finalPercent, 2, '<0.01'); const displayPercent = formatPercentToLocalizedNumerals(finalPercent, 2, '<0.01');
const displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : formatNumberToLocalizedNumerals(value); const displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : formatNumberToLocalizedNumerals(value, 2);
indicators.push({ indicators.push({
name: name, name: name,
+20 -8
View File
@@ -260,31 +260,43 @@ export enum TransactionExplorerValueMetricType {
SourceAmountSum = 'sourceAmountSum', SourceAmountSum = 'sourceAmountSum',
SourceAmountAverage = 'sourceAmountAverage', SourceAmountAverage = 'sourceAmountAverage',
SourceAmountMedian = 'sourceAmountMedian', SourceAmountMedian = 'sourceAmountMedian',
SourceAmount90thPercentile = 'source90thPercentileAmount',
SourceAmountMinimum = 'sourceAmountMinimum', SourceAmountMinimum = 'sourceAmountMinimum',
SourceAmountMaximum = 'sourceAmountMaximum' SourceAmountMaximum = 'sourceAmountMaximum',
SourceAmountRange = 'sourceAmountRange',
SourceAmountInterquartileRange = 'sourceAmountInterquartileRange',
SourceAmountVariance = 'sourceAmountVariance',
SourceAmountStandardDeviation = 'sourceAmountStandardDeviation'
} }
export class TransactionExplorerValueMetric implements NameValue { export class TransactionExplorerValueMetric implements NameValue {
private static readonly allInstances: TransactionExplorerValueMetric[] = []; private static readonly allInstances: TransactionExplorerValueMetric[] = [];
private static readonly allInstancesByValue: Record<string, TransactionExplorerValueMetric> = {}; private static readonly allInstancesByValue: Record<string, TransactionExplorerValueMetric> = {};
public static readonly TransactionCount = new TransactionExplorerValueMetric('Transaction Count', TransactionExplorerValueMetricType.TransactionCount, false); public static readonly TransactionCount = new TransactionExplorerValueMetric('Transaction Count', TransactionExplorerValueMetricType.TransactionCount, false, true);
public static readonly SourceAmountSum = new TransactionExplorerValueMetric('Total Amount', TransactionExplorerValueMetricType.SourceAmountSum, true); public static readonly SourceAmountSum = new TransactionExplorerValueMetric('Total Amount', TransactionExplorerValueMetricType.SourceAmountSum, true, true);
public static readonly SourceAmountAverage = new TransactionExplorerValueMetric('Average Amount', TransactionExplorerValueMetricType.SourceAmountAverage, true); public static readonly SourceAmountAverage = new TransactionExplorerValueMetric('Average Amount', TransactionExplorerValueMetricType.SourceAmountAverage, true, true);
public static readonly SourceAmountMedian = new TransactionExplorerValueMetric('Median Amount', TransactionExplorerValueMetricType.SourceAmountMedian, true); public static readonly SourceAmountMedian = new TransactionExplorerValueMetric('Median Amount', TransactionExplorerValueMetricType.SourceAmountMedian, true, true);
public static readonly SourceAmountMinimum = new TransactionExplorerValueMetric('Minimum Amount', TransactionExplorerValueMetricType.SourceAmountMinimum, true); public static readonly SourceAmount90thPercentile = new TransactionExplorerValueMetric('90th Percentile Amount', TransactionExplorerValueMetricType.SourceAmount90thPercentile, true, true);
public static readonly SourceAmountMaximum = new TransactionExplorerValueMetric('Maximum Amount', TransactionExplorerValueMetricType.SourceAmountMaximum, true); public static readonly SourceAmountMinimum = new TransactionExplorerValueMetric('Minimum Amount', TransactionExplorerValueMetricType.SourceAmountMinimum, true, true);
public static readonly SourceAmountMaximum = new TransactionExplorerValueMetric('Maximum Amount', TransactionExplorerValueMetricType.SourceAmountMaximum, true, true);
public static readonly SourceAmountRange = new TransactionExplorerValueMetric('Range (Max - Min)', TransactionExplorerValueMetricType.SourceAmountRange, true, true);
public static readonly SourceAmountInterquartileRange = new TransactionExplorerValueMetric('Interquartile Range (Q3 - Q1)', TransactionExplorerValueMetricType.SourceAmountInterquartileRange, true, true);
public static readonly SourceAmountVariance = new TransactionExplorerValueMetric('Variance', TransactionExplorerValueMetricType.SourceAmountVariance, false, false);
public static readonly SourceAmountStandardDeviation = new TransactionExplorerValueMetric('Standard Deviation', TransactionExplorerValueMetricType.SourceAmountStandardDeviation, false, false);
public static readonly Default = TransactionExplorerValueMetric.SourceAmountSum; public static readonly Default = TransactionExplorerValueMetric.SourceAmountSum;
public readonly name: string; public readonly name: string;
public readonly value: TransactionExplorerValueMetricType; public readonly value: TransactionExplorerValueMetricType;
public readonly isAmount: boolean; public readonly isAmount: boolean;
public readonly supportSum: boolean;
private constructor(name: string, value: TransactionExplorerValueMetricType, isAmount: boolean) { private constructor(name: string, value: TransactionExplorerValueMetricType, isAmount: boolean, supportSum: boolean) {
this.name = name; this.name = name;
this.value = value; this.value = value;
this.isAmount = isAmount; this.isAmount = isAmount;
this.supportSum = supportSum;
TransactionExplorerValueMetric.allInstances.push(this); TransactionExplorerValueMetric.allInstances.push(this);
TransactionExplorerValueMetric.allInstancesByValue[value] = this; TransactionExplorerValueMetric.allInstancesByValue[value] = this;
+28
View File
@@ -818,10 +818,38 @@ export const useExplorersStore = defineStore('explorers', () => {
} else { } else {
value = 0; value = 0;
} }
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmount90thPercentile) {
if (allSourceAmountsInDefaultCurrency.length > 0) {
allSourceAmountsInDefaultCurrency.sort((a, b) => a - b);
value = allSourceAmountsInDefaultCurrency[Math.floor(allSourceAmountsInDefaultCurrency.length * 9 / 10)] as number;
} else {
value = 0;
}
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountMinimum) { } else if (valueMetric === TransactionExplorerValueMetric.SourceAmountMinimum) {
value = minimumSourceAmountInDefaultCurrency === Number.MAX_SAFE_INTEGER ? 0 : minimumSourceAmountInDefaultCurrency; value = minimumSourceAmountInDefaultCurrency === Number.MAX_SAFE_INTEGER ? 0 : minimumSourceAmountInDefaultCurrency;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountMaximum) { } else if (valueMetric === TransactionExplorerValueMetric.SourceAmountMaximum) {
value = maximumSourceAmountInDefaultCurrency === Number.MIN_SAFE_INTEGER ? 0 : maximumSourceAmountInDefaultCurrency; value = maximumSourceAmountInDefaultCurrency === Number.MIN_SAFE_INTEGER ? 0 : maximumSourceAmountInDefaultCurrency;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountRange) {
const finalMinimumSourceAmountInDefaultCurrency = minimumSourceAmountInDefaultCurrency === Number.MAX_SAFE_INTEGER ? 0 : minimumSourceAmountInDefaultCurrency;
const finalMaximumSourceAmountInDefaultCurrency = maximumSourceAmountInDefaultCurrency === Number.MIN_SAFE_INTEGER ? 0 : maximumSourceAmountInDefaultCurrency;
value = finalMaximumSourceAmountInDefaultCurrency - finalMinimumSourceAmountInDefaultCurrency;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountInterquartileRange) {
if (allSourceAmountsInDefaultCurrency.length > 0) {
allSourceAmountsInDefaultCurrency.sort((a, b) => a - b);
const q1 = allSourceAmountsInDefaultCurrency[Math.floor(allSourceAmountsInDefaultCurrency.length / 4)] as number;
const q3 = allSourceAmountsInDefaultCurrency[Math.floor(allSourceAmountsInDefaultCurrency.length * 3 / 4)] as number;
value = q3 - q1;
} else {
value = 0;
}
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountVariance) {
const averageSourceAmountInDefaultCurrency = allSourceAmountsInDefaultCurrency.length > 0 ? totalSourceAmountSumInDefaultCurrency / allSourceAmountsInDefaultCurrency.length / 100.0 : 0;
const sumOfSquaredDifferences = allSourceAmountsInDefaultCurrency.reduce((sum, amount) => sum + Math.pow(amount / 100.0 - averageSourceAmountInDefaultCurrency, 2), 0);
value = allSourceAmountsInDefaultCurrency.length > 0 ? sumOfSquaredDifferences / allSourceAmountsInDefaultCurrency.length : 0;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountStandardDeviation) {
const averageSourceAmountInDefaultCurrency = allSourceAmountsInDefaultCurrency.length > 0 ? totalSourceAmountSumInDefaultCurrency / allSourceAmountsInDefaultCurrency.length / 100.0 : 0;
const sumOfSquaredDifferences = allSourceAmountsInDefaultCurrency.reduce((sum, amount) => sum + Math.pow(amount / 100.0 - averageSourceAmountInDefaultCurrency, 2), 0);
value = allSourceAmountsInDefaultCurrency.length > 0 ? Math.sqrt(sumOfSquaredDifferences / allSourceAmountsInDefaultCurrency.length) : 0;
} }
dataItems.push({ dataItems.push({
@@ -92,7 +92,7 @@
:show-value="true" :show-value="true"
:show-percent="true" :show-percent="true"
:enable-click-item="true" :enable-click-item="true"
:amount-value="currentExplorer.valueMetric !== TransactionExplorerValueMetric.TransactionCount.value" :amount-value="TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.isAmount"
:default-currency="defaultCurrency" :default-currency="defaultCurrency"
id-field="id" id-field="id"
name-field="name" name-field="name"
@@ -120,7 +120,7 @@
:items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []" :items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []"
:show-value="true" :show-value="true"
:show-percent="true" :show-percent="true"
:amount-value="currentExplorer.valueMetric !== TransactionExplorerValueMetric.TransactionCount.value" :amount-value="TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.isAmount"
:default-currency="defaultCurrency" :default-currency="defaultCurrency"
name-field="name" name-field="name"
value-field="totalAmount" value-field="totalAmount"
@@ -146,12 +146,12 @@
:one-hundred-percent-stacked="axisChart100PercentStacked" :one-hundred-percent-stacked="axisChart100PercentStacked"
:sorting-type="currentExplorer.chartSortingType" :sorting-type="currentExplorer.chartSortingType"
:show-value="true" :show-value="true"
:show-total-amount-in-tooltip="true" :show-total-amount-in-tooltip="TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.supportSum"
:total-name-in-tooltip="currentExplorer.valueMetric === TransactionExplorerValueMetric.TransactionCount.value ? tt('Total Transactions') : tt('Total Amount')" :total-name-in-tooltip="currentExplorer.valueMetric === TransactionExplorerValueMetric.TransactionCount.value ? tt('Total Transactions') : tt('Total Amount')"
:category-type-name="currentTransactionExplorerCategoryDimensionName" :category-type-name="currentTransactionExplorerCategoryDimensionName"
:all-category-names="categoriedNamesSortedByDisplayOrder" :all-category-names="categoriedNamesSortedByDisplayOrder"
:items="seriesDimensionTransactionExplorerData" :items="seriesDimensionTransactionExplorerData"
:amount-value="currentExplorer.valueMetric !== TransactionExplorerValueMetric.TransactionCount.value" :amount-value="TransactionExplorerValueMetric.valueOf(currentExplorer.valueMetric)?.isAmount"
:default-currency="defaultCurrency" :default-currency="defaultCurrency"
:enable-click-item="true" :enable-click-item="true"
id-field="id" id-field="id"