add skewness and kurtosis to value metric in insights explorer
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = '+';
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "今月",
|
||||
|
||||
@@ -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": "ಈ ತಿಂಗಳು",
|
||||
|
||||
@@ -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": "이번 달",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "В этом месяце",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "இந்த மாதம்",
|
||||
|
||||
@@ -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": "เดือนนี้",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "Цього місяця",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1824,6 +1824,8 @@
|
||||
"Variance": "方差",
|
||||
"Standard Deviation": "标准差",
|
||||
"Coefficient of Variation": "变异系数",
|
||||
"Skewness": "偏度",
|
||||
"Kurtosis": "峰度",
|
||||
"Account List": "账户列表",
|
||||
"This Week": "本周",
|
||||
"This Month": "本月",
|
||||
|
||||
@@ -1824,6 +1824,8 @@
|
||||
"Variance": "變異數",
|
||||
"Standard Deviation": "標準差",
|
||||
"Coefficient of Variation": "變異係數",
|
||||
"Skewness": "偏度",
|
||||
"Kurtosis": "峰度",
|
||||
"Account List": "帳戶清單",
|
||||
"This Week": "本週",
|
||||
"This Month": "本月",
|
||||
|
||||
+35
-38
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user