add skewness and kurtosis to value metric in insights explorer

This commit is contained in:
MaysWind
2026-04-16 01:24:07 +08:00
parent 02d8b132f5
commit 7a821abbb6
30 changed files with 181 additions and 54 deletions
+3 -2
View File
@@ -81,8 +81,9 @@ import {
import { NumeralSystem, DecimalSeparator } from '@/core/numeral.ts';
import type { CurrencyPrependAndAppendText } from '@/core/currency.ts';
import { DEFAULT_DECIMAL_NUMBER_COUNT } from '@/consts/numeral.ts';
import { DEFAULT_DECIMAL_NUMBER_COUNT, AMOUNT_FACTOR } from '@/consts/numeral.ts';
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts';
import { isNumber, replaceAll } from '@/lib/common.ts';
import { evaluateExpressionToAmount } from '@/lib/evaluator.ts';
import type { ComponentDensity, InputVariant } from '@/lib/ui/desktop.ts';
@@ -297,7 +298,7 @@ function getFormattedValue(value: number): string {
function getDisplayCurrencyPrependAndAppendText(): CurrencyPrependAndAppendText | null {
const numericCurrentValue = parseAmountFromLocalizedNumerals(currentValue.value);
const isPlural = numericCurrentValue !== 100 && numericCurrentValue !== -100;
const isPlural = numericCurrentValue !== AMOUNT_FACTOR && numericCurrentValue !== -AMOUNT_FACTOR;
return getAmountPrependAndAppendText(props.currency, isPlural);
}
+2 -1
View File
@@ -83,6 +83,7 @@ import { useI18n } from '@/locales/helpers.ts';
import { useI18nUIComponents, isiOS } from '@/lib/ui/mobile.ts';
import { NumeralSystem } from '@/core/numeral.ts';
import { AMOUNT_FACTOR } from '@/consts/numeral.ts';
import { ALL_CURRENCIES } from '@/consts/currency.ts';
import { isNumber } from '@/lib/common.ts';
import logger from '@/lib/logger.ts';
@@ -385,7 +386,7 @@ function confirm(): boolean {
finalValue = previous - current;
break;
case '×':
finalValue = Math.trunc(previous * current / 100);
finalValue = Math.trunc(previous * current / AMOUNT_FACTOR);
break;
default:
finalValue = previous;
+1
View File
@@ -2,6 +2,7 @@ import type { HiddenAmount } from '@/core/numeral.ts';
export const DEFAULT_DECIMAL_NUMBER_COUNT: number = 2;
export const MAX_SUPPORTED_DECIMAL_NUMBER_COUNT: number = 2;
export const AMOUNT_FACTOR: number = 10 ** MAX_SUPPORTED_DECIMAL_NUMBER_COUNT;
export const DISPLAY_HIDDEN_AMOUNT: HiddenAmount = '***';
export const INCOMPLETE_AMOUNT_SUFFIX: string = '+';
+5 -1
View File
@@ -324,7 +324,9 @@ export enum TransactionExplorerValueMetricType {
SourceAmountInterquartileRange = 'sourceAmountInterquartileRange',
SourceAmountVariance = 'sourceAmountVariance',
SourceAmountStandardDeviation = 'sourceAmountStandardDeviation',
SourceAmountCoefficientOfVariation = 'sourceAmountCoefficientOfVariation'
SourceAmountCoefficientOfVariation = 'sourceAmountCoefficientOfVariation',
SourceAmountSkewness = 'sourceAmountSkewness',
SourceAmountKurtosis = 'sourceAmountKurtosis'
}
export class TransactionExplorerValueMetric implements NameValue {
@@ -356,6 +358,8 @@ export class TransactionExplorerValueMetric implements NameValue {
public static readonly SourceAmountVariance = new TransactionExplorerValueMetric('Variance', TransactionExplorerValueMetricType.SourceAmountVariance, false, false, false);
public static readonly SourceAmountStandardDeviation = new TransactionExplorerValueMetric('Standard Deviation', TransactionExplorerValueMetricType.SourceAmountStandardDeviation, false, false, false);
public static readonly SourceAmountCoefficientOfVariation = new TransactionExplorerValueMetric('Coefficient of Variation', TransactionExplorerValueMetricType.SourceAmountCoefficientOfVariation, false, false, false);
public static readonly SourceAmountSkewness = new TransactionExplorerValueMetric('Skewness', TransactionExplorerValueMetricType.SourceAmountSkewness, false, false, false);
public static readonly SourceAmountKurtosis = new TransactionExplorerValueMetric('Kurtosis', TransactionExplorerValueMetricType.SourceAmountKurtosis, false, false, false);
public static readonly Default = TransactionExplorerValueMetric.SourceAmountSum;
+2 -1
View File
@@ -1,3 +1,4 @@
import { AMOUNT_FACTOR } from '@/consts/numeral.ts';
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '../consts/transaction.ts';
import { replaceAll } from './common.ts';
@@ -10,7 +11,7 @@ type OperatorAndParenthesis = Operator | '(' | ')';
const maxAllowedDecimalCount = 6;
const normalizeFactor: number = 1000000;
const normalizedDecimalsMaxZeroString: string = '000000';
const normalizedNumberToAmountFactor: number = 10000; // 1000000 / 100
const normalizedNumberToAmountFactor: number = normalizeFactor / AMOUNT_FACTOR;
const operatorPriority: Record<Operator, number> = {
'+': 1,
+79
View File
@@ -1,3 +1,5 @@
import { reversed } from '@/core/base.ts';
export function mean<T>(values: T[], valueFn: (item: T) => number): number {
if (values.length < 1) {
return 0;
@@ -59,3 +61,80 @@ export function sumMaxN<T>(sortedValues: T[], n: number, valueFn: (item: T) => n
return sum;
}
export function cumulativePercentage<T>(sortedValues: T[], percentageThreshold: number, totalValue: number, valueFn: (item: T) => number): number {
if (sortedValues.length < 1 || percentageThreshold < 0 || percentageThreshold > 1) {
return 0;
}
const thresholdValue: number = percentageThreshold * totalValue;
let cumulativeValue: number = 0;
let cumulativeCount: number = 0;
for (const item of reversed(sortedValues)) {
cumulativeValue += valueFn(item);
cumulativeCount++;
if (cumulativeValue >= thresholdValue) {
return 100.0 * cumulativeCount / sortedValues.length;
}
}
return 0;
}
export function varianceAndStandardDeviation<T>(values: T[], meanValue: number, valueFn: (item: T) => number): { variance: number; standardDeviation: number } {
if (values.length < 1) {
return { variance: 0, standardDeviation: 0 };
}
let sumOfSquaredDifferences: number = 0;
for (const item of values) {
const difference: number = valueFn(item) - meanValue;
sumOfSquaredDifferences += difference * difference;
}
const variance: number = sumOfSquaredDifferences / values.length;
const standardDeviation: number = Math.sqrt(variance);
return { variance, standardDeviation };
}
export function coefficientOfVariation(standardDeviation: number, meanValue: number): number | undefined {
if (meanValue === 0) {
return undefined;
}
return standardDeviation / meanValue;
}
export function skewness<T>(values: T[], meanValue: number, standardDeviation: number, valueFn: (item: T) => number): number {
if (values.length < 1 || standardDeviation === 0) {
return 0;
}
let sumOfCubedDifferences: number = 0;
for (const item of values) {
const difference: number = valueFn(item) - meanValue;
sumOfCubedDifferences += Math.pow(difference, 3);
}
return sumOfCubedDifferences / (values.length * Math.pow(standardDeviation, 3));
}
export function kurtosis<T>(values: T[], meanValue: number, variance: number, valueFn: (item: T) => number): number {
if (values.length < 1 || variance === 0) {
return 0;
}
let sumOfQuarticDifferences: number = 0;
for (const item of values) {
const difference: number = valueFn(item) - meanValue;
sumOfQuarticDifferences += Math.pow(difference, 4);
}
return sumOfQuarticDifferences / (values.length * Math.pow(variance, 2));
}
+10 -7
View File
@@ -6,6 +6,8 @@ import {
DigitGroupingSymbol
} from '@/core/numeral.ts';
import { AMOUNT_FACTOR } from '@/consts/numeral.ts';
import { DEFAULT_DECIMAL_NUMBER_COUNT, MAX_SUPPORTED_DECIMAL_NUMBER_COUNT, DISPLAY_HIDDEN_AMOUNT } from '@/consts/numeral.ts';
import { isDefined, isString, isNumber, replaceAll, removeAll } from './common.ts';
@@ -115,7 +117,7 @@ export function parseAmount(str: string, options: NumberFormatOptions): number {
let decimalSeparatorPos = str.indexOf(decimalSeparator);
if (decimalSeparatorPos < 0) {
return sign * numeralSystem.parseInt(str) * 100;
return sign * numeralSystem.parseInt(str) * AMOUNT_FACTOR;
} else if (decimalSeparatorPos === 0) {
str = numeralSystem.digitZero + str;
decimalSeparatorPos++;
@@ -125,13 +127,13 @@ export function parseAmount(str: string, options: NumberFormatOptions): number {
const decimals = str.substring(decimalSeparatorPos + 1, str.length);
if (decimals.length < 1) {
return sign * numeralSystem.parseInt(integer) * 100;
return sign * numeralSystem.parseInt(integer) * AMOUNT_FACTOR;
} else if (decimals.length === 1) {
return sign * numeralSystem.parseInt(integer) * 100 + sign * numeralSystem.parseInt(decimals) * 10;
return sign * numeralSystem.parseInt(integer) * AMOUNT_FACTOR + sign * numeralSystem.parseInt(decimals) * AMOUNT_FACTOR / 10;
} else if (decimals.length === 2) {
return sign * numeralSystem.parseInt(integer) * 100 + sign * numeralSystem.parseInt(decimals);
return sign * numeralSystem.parseInt(integer) * AMOUNT_FACTOR + sign * numeralSystem.parseInt(decimals);
} else {
return sign * numeralSystem.parseInt(integer) * 100 + sign * numeralSystem.parseInt(decimals.substring(0, 2));
return sign * numeralSystem.parseInt(integer) * AMOUNT_FACTOR + sign * numeralSystem.parseInt(decimals.substring(0, 2));
}
}
@@ -252,9 +254,10 @@ export function formatPercent(value: number, precision: number, lowPrecisionValu
export function getAmountWithDecimalNumberCount(amount: number, decimalNumberCount: number): number {
if (decimalNumberCount === 0) {
return Math.trunc(amount / 100) * 100;
return Math.trunc(amount / AMOUNT_FACTOR) * AMOUNT_FACTOR;
} else if (decimalNumberCount === 1) {
return Math.trunc(amount / 10) * 10;
const factor = AMOUNT_FACTOR / 10;
return Math.trunc(amount / factor) * factor;
}
return amount;
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Varianz",
"Standard Deviation": "Standardabweichung",
"Coefficient of Variation": "Variationskoeffizient",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Kontoliste",
"This Week": "Diese Woche",
"This Month": "Dieser Monat",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Account List",
"This Week": "This Week",
"This Month": "This Month",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Lista de Cuentas",
"This Week": "Esta Semana",
"This Month": "Este Mes",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Liste des comptes",
"This Week": "Cette semaine",
"This Month": "Ce mois",
+2 -2
View File
@@ -170,7 +170,7 @@ import {
import type { LocaleDefaultSettings } from '@/core/setting.ts';
import type { ErrorResponse } from '@/core/api.ts';
import { DISPLAY_HIDDEN_AMOUNT, INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numeral.ts';
import { AMOUNT_FACTOR, DISPLAY_HIDDEN_AMOUNT, INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numeral.ts';
import { UTC_TIMEZONE, ALL_TIMEZONES } from '@/consts/timezone.ts';
import { ALL_CURRENCIES } from '@/consts/currency.ts';
import { DEFAULT_EXPENSE_CATEGORIES, DEFAULT_INCOME_CATEGORIES, DEFAULT_TRANSFER_CATEGORIES } from '@/consts/category.ts';
@@ -2189,7 +2189,7 @@ export function useI18n() {
const currencyName = getCurrencyName(finalCurrencyCode);
if (isNumber(value)) {
const isPlural: boolean = value !== 100 && value !== -100;
const isPlural: boolean = value !== AMOUNT_FACTOR && value !== -AMOUNT_FACTOR;
const textualValue = formatAmount(value, numberFormatOptions);
if (!finalCurrencyCode) {
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Elenco account",
"This Week": "Questa settimana",
"This Month": "Questo mese",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "口座リスト",
"This Week": "今週",
"This Month": "今月",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "ಖಾತೆಗಳ ಪಟ್ಟಿ",
"This Week": "ಈ ವಾರ",
"This Month": "ಈ ತಿಂಗಳು",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "계좌 목록",
"This Week": "이번 주",
"This Month": "이번 달",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Rekeningenlijst",
"This Week": "Deze week",
"This Month": "Deze maand",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variância",
"Standard Deviation": "Desvio Padrão",
"Coefficient of Variation": "Coeficiente de Variação",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Lista de Contas",
"This Week": "Esta Semana",
"This Month": "Este Mês",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Список счетов",
"This Week": "На этой неделе",
"This Month": "В этом месяце",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Seznam računov",
"This Week": "Ta teden",
"This Month": "Ta mesec",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "கணக்குகளின் பட்டியல்",
"This Week": "இந்த வாரம்",
"This Month": "இந்த மாதம்",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "รายการบัญชี",
"This Week": "สัปดาห์นี้",
"This Month": "เดือนนี้",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Hesap Listesi",
"This Week": "Bu Hafta",
"This Month": "Bu Ay",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Список рахунків",
"This Week": "Цього тижня",
"This Month": "Цього місяця",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "Variance",
"Standard Deviation": "Standard Deviation",
"Coefficient of Variation": "Coefficient of Variation",
"Skewness": "Skewness",
"Kurtosis": "Kurtosis",
"Account List": "Danh sách tài khoản",
"This Week": "Tuần này",
"This Month": "Tháng này",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "方差",
"Standard Deviation": "标准差",
"Coefficient of Variation": "变异系数",
"Skewness": "偏度",
"Kurtosis": "峰度",
"Account List": "账户列表",
"This Week": "本周",
"This Month": "本月",
+2
View File
@@ -1824,6 +1824,8 @@
"Variance": "變異數",
"Standard Deviation": "標準差",
"Coefficient of Variation": "變異係數",
"Skewness": "偏度",
"Kurtosis": "峰度",
"Account List": "帳戶清單",
"This Week": "本週",
"This Month": "本月",
+35 -38
View File
@@ -8,7 +8,7 @@ import { useTransactionCategoriesStore } from './transactionCategory.ts';
import { useTransactionTagsStore } from './transactionTag.ts';
import { useExchangeRatesStore } from './exchangeRates.ts';
import { type BeforeResolveFunction, itemAndIndex, reversed, keys, values } from '@/core/base.ts';
import { type BeforeResolveFunction, itemAndIndex, keys, values } from '@/core/base.ts';
import { NumeralSystem, AmountFilterType } from '@/core/numeral.ts';
import { type DateTime, DateRangeScene, DateRange } from '@/core/datetime.ts';
import { TimezoneTypeForStatistics } from '@/core/timezone.ts';
@@ -20,6 +20,7 @@ import {
TransactionExplorerValueMetric,
DEFAULT_TRANSACTION_EXPLORER_DATE_RANGE
} from '@/core/explorer.ts';
import { AMOUNT_FACTOR } from '@/consts/numeral.ts';
import { ALL_CURRENCIES } from '@/consts/currency.ts';
import { type Account } from '@/models/account.ts';
@@ -46,7 +47,12 @@ import {
import {
median,
percentile,
sumMaxN
sumMaxN,
cumulativePercentage,
varianceAndStandardDeviation,
coefficientOfVariation,
skewness,
kurtosis
} from '@/lib/math.ts';
import {
getUtcOffsetByUtcOffsetMinutes,
@@ -726,26 +732,15 @@ export const useExplorersStore = defineStore('explorers', () => {
}
if (sourceAmounts.length > 0) {
const eightyPercentAmountThreshold: number = 0.8 * statisticData.totalAmount;
let cumulativeAmount: number = 0;
let cumulativeCount: number = 0;
for (const amount of reversed(sourceAmounts)) {
cumulativeAmount += amount;
cumulativeCount++;
if (cumulativeAmount >= eightyPercentAmountThreshold) {
statisticData.transactionsFor80PercentAmount = 100.0 * cumulativeCount / sourceAmounts.length;
break;
}
}
statisticData.transactionsFor80PercentAmount = cumulativePercentage(sourceAmounts, 0.8, statisticData.totalAmount, item => item);
}
if (sourceAmounts.length > 0) {
const averageAmountForVarianceCalculation: number = statisticData.totalAmount / sourceAmounts.length / 100.0;
const sumOfSquaredDifferences: number = sourceAmounts.reduce((sum, amount) => sum + Math.pow(amount / 100.0 - averageAmountForVarianceCalculation, 2), 0);
statisticData.variance = sumOfSquaredDifferences / sourceAmounts.length;
statisticData.standardDeviation = Math.sqrt(statisticData.variance);
statisticData.coefficientOfVariation = averageAmountForVarianceCalculation !== 0 ? statisticData.standardDeviation / averageAmountForVarianceCalculation : undefined;
const averageAmountForVarianceCalculation: number = statisticData.totalAmount / sourceAmounts.length / AMOUNT_FACTOR;
const { variance, standardDeviation } = varianceAndStandardDeviation(sourceAmounts, averageAmountForVarianceCalculation, item => item / AMOUNT_FACTOR);
statisticData.variance = variance;
statisticData.standardDeviation = standardDeviation;
statisticData.coefficientOfVariation = coefficientOfVariation(standardDeviation, averageAmountForVarianceCalculation);
}
return statisticData;
@@ -892,7 +887,12 @@ export const useExplorersStore = defineStore('explorers', () => {
} else {
value = 0;
}
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountQ1Amount || valueMetric === TransactionExplorerValueMetric.SourceAmountQ3Amount || valueMetric === TransactionExplorerValueMetric.SourceAmount10thPercentile || valueMetric === TransactionExplorerValueMetric.SourceAmount90thPercentile || valueMetric === TransactionExplorerValueMetric.SourceAmount95thPercentile || valueMetric === TransactionExplorerValueMetric.SourceAmount99thPercentile) {
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountQ1Amount
|| valueMetric === TransactionExplorerValueMetric.SourceAmountQ3Amount
|| valueMetric === TransactionExplorerValueMetric.SourceAmount10thPercentile
|| valueMetric === TransactionExplorerValueMetric.SourceAmount90thPercentile
|| valueMetric === TransactionExplorerValueMetric.SourceAmount95thPercentile
|| valueMetric === TransactionExplorerValueMetric.SourceAmount99thPercentile) {
if (allSourceAmountsInDefaultCurrency.length > 0) {
allSourceAmountsInDefaultCurrency.sort((a, b) => a - b);
@@ -932,18 +932,7 @@ export const useExplorersStore = defineStore('explorers', () => {
} else if (valueMetric === TransactionExplorerValueMetric.TransactionsForEightyPercentOfSourceAmount) {
if (allSourceAmountsInDefaultCurrency.length > 0) {
allSourceAmountsInDefaultCurrency.sort((a, b) => a - b);
const eightyPercentAmountThreshold: number = 0.8 * totalSourceAmountSumInDefaultCurrency;
let cumulativeAmount: number = 0;
let cumulativeCount: number = 0;
for (const amount of reversed(allSourceAmountsInDefaultCurrency)) {
cumulativeAmount += amount;
cumulativeCount++;
if (cumulativeAmount >= eightyPercentAmountThreshold) {
value = 100.0 * cumulativeCount / allSourceAmountsInDefaultCurrency.length;
break;
}
}
value = cumulativePercentage(allSourceAmountsInDefaultCurrency, 0.8, totalSourceAmountSumInDefaultCurrency, item => item);
} else {
value = 0;
}
@@ -962,17 +951,25 @@ export const useExplorersStore = defineStore('explorers', () => {
} else {
value = 0;
}
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountVariance || valueMetric === TransactionExplorerValueMetric.SourceAmountStandardDeviation || valueMetric === TransactionExplorerValueMetric.SourceAmountCoefficientOfVariation) {
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountVariance
|| valueMetric === TransactionExplorerValueMetric.SourceAmountStandardDeviation
|| valueMetric === TransactionExplorerValueMetric.SourceAmountCoefficientOfVariation
|| valueMetric === TransactionExplorerValueMetric.SourceAmountSkewness
|| valueMetric === TransactionExplorerValueMetric.SourceAmountKurtosis) {
if (allSourceAmountsInDefaultCurrency.length > 0) {
const averageSourceAmountInDefaultCurrency = totalSourceAmountSumInDefaultCurrency / allSourceAmountsInDefaultCurrency.length / 100.0;
const sumOfSquaredDifferences = allSourceAmountsInDefaultCurrency.reduce((sum, amount) => sum + Math.pow(amount / 100.0 - averageSourceAmountInDefaultCurrency, 2), 0);
const averageSourceAmountInDefaultCurrency = totalSourceAmountSumInDefaultCurrency / allSourceAmountsInDefaultCurrency.length / AMOUNT_FACTOR;
const { variance, standardDeviation } = varianceAndStandardDeviation(allSourceAmountsInDefaultCurrency, averageSourceAmountInDefaultCurrency, item => item / AMOUNT_FACTOR);
if (valueMetric === TransactionExplorerValueMetric.SourceAmountVariance) {
value = sumOfSquaredDifferences / allSourceAmountsInDefaultCurrency.length
value = variance;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountStandardDeviation) {
value = Math.sqrt(sumOfSquaredDifferences / allSourceAmountsInDefaultCurrency.length);
value = standardDeviation;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountCoefficientOfVariation) {
value = averageSourceAmountInDefaultCurrency !== 0 ? Math.sqrt(sumOfSquaredDifferences / allSourceAmountsInDefaultCurrency.length) / averageSourceAmountInDefaultCurrency : 0;
value = coefficientOfVariation(standardDeviation, averageSourceAmountInDefaultCurrency) ?? 0;
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountSkewness) {
value = skewness(allSourceAmountsInDefaultCurrency, averageSourceAmountInDefaultCurrency, standardDeviation, item => item / AMOUNT_FACTOR);
} else if (valueMetric === TransactionExplorerValueMetric.SourceAmountKurtosis) {
value = kurtosis(allSourceAmountsInDefaultCurrency, averageSourceAmountInDefaultCurrency, variance, item => item / AMOUNT_FACTOR);
}
} else {
value = 0;
+2 -1
View File
@@ -171,6 +171,7 @@ import { useExchangeRatesPageBase } from '@/views/base/ExchangeRatesPageBase.ts'
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
import { NumeralSystem } from '@/core/numeral.ts';
import { AMOUNT_FACTOR } from '@/consts/numeral.ts';
import type { LocalizedLatestExchangeRate } from '@/models/exchange_rate.ts';
@@ -302,7 +303,7 @@ function getFinalConvertedAmount(toExchangeRate: LocalizedLatestExchangeRate, di
let exchangeRateAmount: number | '' | null = 0;
try {
exchangeRateAmount = getConvertedAmount(baseAmount.value / 100, fromExchangeRate, toExchangeRate);
exchangeRateAmount = getConvertedAmount(baseAmount.value / AMOUNT_FACTOR, fromExchangeRate, toExchangeRate);
} catch (ex) {
exchangeRateAmount = 0;
logger.warn('failed to convert amount by exchange rates, original base amount is ' + baseAmount.value, ex)
+2 -1
View File
@@ -138,6 +138,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
import { TextDirection } from '@/core/text.ts';
import { NumeralSystem } from '@/core/numeral.ts';
import { AMOUNT_FACTOR } from '@/consts/numeral.ts';
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts';
import type { LocalizedLatestExchangeRate } from '@/models/exchange_rate.ts';
@@ -277,7 +278,7 @@ function remove(customExchangeRate: LocalizedLatestExchangeRate | null, confirm:
function getFinalConvertedAmount(toExchangeRate: LocalizedLatestExchangeRate, displayLocalizedDigits: boolean): string {
const fromExchangeRate = exchangeRatesStore.latestExchangeRateMap[baseCurrency.value];
const exchangeRateAmount = getConvertedAmount(baseAmount.value / 100, fromExchangeRate, toExchangeRate);
const exchangeRateAmount = getConvertedAmount(baseAmount.value / AMOUNT_FACTOR, fromExchangeRate, toExchangeRate);
if (!exchangeRateAmount) {
if (displayLocalizedDigits) {