hide some statistics when the number of transactions is not enough

This commit is contained in:
MaysWind
2026-03-07 21:16:03 +08:00
parent aedebb1461
commit 301fb58917
2 changed files with 31 additions and 28 deletions
+25 -22
View File
@@ -120,14 +120,14 @@ export interface InsightsExplorerTransactionStatisticData {
averageAmount: number; averageAmount: number;
medianAmount: number; medianAmount: number;
p90Amount: number; p90Amount: number;
top5AmountShare: number; top5AmountShare?: number;
transactionsFor80PercentAmount: number; transactionsFor80PercentAmount?: number;
minimumAmount: number; minimumAmount: number;
maximumAmount: number; maximumAmount: number;
range: number; range: number;
interquartileRange: number; interquartileRange: number;
variance: number; variance?: number;
standardDeviation: number; standardDeviation?: number;
} }
export const useExplorersStore = defineStore('explorers', () => { export const useExplorersStore = defineStore('explorers', () => {
@@ -607,14 +607,14 @@ export const useExplorersStore = defineStore('explorers', () => {
averageAmount: 0, averageAmount: 0,
medianAmount: 0, medianAmount: 0,
p90Amount: 0, p90Amount: 0,
top5AmountShare: 0, top5AmountShare: undefined,
transactionsFor80PercentAmount: 0, transactionsFor80PercentAmount: undefined,
minimumAmount: Number.MAX_SAFE_INTEGER, minimumAmount: Number.MAX_SAFE_INTEGER,
maximumAmount: Number.MIN_SAFE_INTEGER, maximumAmount: Number.MIN_SAFE_INTEGER,
range: 0, range: 0,
interquartileRange: 0, interquartileRange: 0,
variance: 0, variance: undefined,
standardDeviation: 0 standardDeviation: undefined
}; };
const sourceAmounts: number[] = []; const sourceAmounts: number[] = [];
@@ -670,26 +670,29 @@ export const useExplorersStore = defineStore('explorers', () => {
const top5Count = Math.ceil(sourceAmounts.length * 0.05); const top5Count = Math.ceil(sourceAmounts.length * 0.05);
const top5AmountSum = sourceAmounts.slice(-top5Count).reduce((sum, amount) => sum + amount, 0); const top5AmountSum = sourceAmounts.slice(-top5Count).reduce((sum, amount) => sum + amount, 0);
statisticData.top5AmountShare = statisticData.totalAmount > 0 ? 100.0 * top5AmountSum / statisticData.totalAmount : 0; statisticData.top5AmountShare = statisticData.totalAmount > 0 ? 100.0 * top5AmountSum / statisticData.totalAmount : 0;
} else {
statisticData.top5AmountShare = 100.0;
} }
const eightyPercentAmountThreshold: number = 0.8 * statisticData.totalAmount; if (sourceAmounts.length > 0) {
let cumulativeAmount: number = 0; const eightyPercentAmountThreshold: number = 0.8 * statisticData.totalAmount;
let cumulativeCount: number = 0; let cumulativeAmount: number = 0;
for (const amount of reversed(sourceAmounts)) { let cumulativeCount: number = 0;
cumulativeAmount += amount; for (const amount of reversed(sourceAmounts)) {
cumulativeCount++; cumulativeAmount += amount;
cumulativeCount++;
if (cumulativeAmount >= eightyPercentAmountThreshold) { if (cumulativeAmount >= eightyPercentAmountThreshold) {
statisticData.transactionsFor80PercentAmount = 100.0 * cumulativeCount / statisticData.totalCount; statisticData.transactionsFor80PercentAmount = 100.0 * cumulativeCount / statisticData.totalCount;
break; break;
}
} }
} }
const sumOfSquaredDifferences: number = sourceAmounts.reduce((sum, amount) => sum + Math.pow(amount / 100.0 - statisticData.averageAmount / 100.0, 2), 0); if (statisticData.totalCount > 0 && sourceAmounts.length > 0) {
statisticData.variance = sourceAmounts.length > 0 ? sumOfSquaredDifferences / sourceAmounts.length : 0; const averageAmountForVarianceCalculation: number = statisticData.totalAmount / statisticData.totalCount / 100.0;
statisticData.standardDeviation = Math.sqrt(statisticData.variance); 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);
}
return statisticData; return statisticData;
}); });
@@ -41,7 +41,7 @@
<span class="text-subtitle-1 ms-2" v-else-if="!loading && filteredTransactionsStatistic"> <span class="text-subtitle-1 ms-2" v-else-if="!loading && filteredTransactionsStatistic">
{{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.totalAmount) }} {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.totalAmount) }}
</span> </span>
<v-tooltip interactive class="table-tooltip" activator="parent" v-if="!loading && filteredTransactionsStatistic"> <v-tooltip interactive class="table-tooltip" activator="parent" v-if="!loading && filteredTransactions.length > 0 && filteredTransactionsStatistic">
<v-table density="compact"> <v-table density="compact">
<tbody> <tbody>
<tr> <tr>
@@ -74,11 +74,11 @@
</tr> </tr>
<tr> <tr>
<td>{{ tt('Top 5 Amount Share') }}</td> <td>{{ tt('Top 5 Amount Share') }}</td>
<td class="text-end">{{ formatPercentToLocalizedNumerals(filteredTransactionsStatistic.top5AmountShare, 2, '<0.01') }}</td> <td class="text-end">{{ isDefined(filteredTransactionsStatistic.top5AmountShare) ? formatPercentToLocalizedNumerals(filteredTransactionsStatistic.top5AmountShare, 2, '<0.01') : '-' }}</td>
</tr> </tr>
<tr> <tr>
<td>{{ tt('Transactions for 80% of Amount') }}</td> <td>{{ tt('Transactions for 80% of Amount') }}</td>
<td class="text-end">{{ formatPercentToLocalizedNumerals(filteredTransactionsStatistic.transactionsFor80PercentAmount, 2, '<0.01') }}</td> <td class="text-end">{{ isDefined(filteredTransactionsStatistic.transactionsFor80PercentAmount) ? formatPercentToLocalizedNumerals(filteredTransactionsStatistic.transactionsFor80PercentAmount, 2, '<0.01') : '-' }}</td>
</tr> </tr>
<tr> <tr>
<td>{{ tt('Minimum Amount') }}</td> <td>{{ tt('Minimum Amount') }}</td>
@@ -98,11 +98,11 @@
</tr> </tr>
<tr> <tr>
<td>{{ tt('Variance') }}</td> <td>{{ tt('Variance') }}</td>
<td class="text-end">{{ formatNumberToLocalizedNumerals(filteredTransactionsStatistic.variance, 2) }}</td> <td class="text-end">{{ isDefined(filteredTransactionsStatistic.variance) ? formatNumberToLocalizedNumerals(filteredTransactionsStatistic.variance, 2) : '-' }}</td>
</tr> </tr>
<tr> <tr>
<td>{{ tt('Standard Deviation') }}</td> <td>{{ tt('Standard Deviation') }}</td>
<td class="text-end">{{ formatNumberToLocalizedNumerals(filteredTransactionsStatistic.standardDeviation, 2) }}</td> <td class="text-end">{{ isDefined(filteredTransactionsStatistic.standardDeviation) ? formatNumberToLocalizedNumerals(filteredTransactionsStatistic.standardDeviation, 2) : '-' }}</td>
</tr> </tr>
</tbody> </tbody>
</v-table> </v-table>
@@ -218,7 +218,7 @@ import { TransactionType } from '@/core/transaction.ts';
import type { TransactionInsightDataItem } from '@/models/transaction.ts'; import type { TransactionInsightDataItem } from '@/models/transaction.ts';
import type { InsightsExplorer} from '@/models/explorer.ts'; import type { InsightsExplorer} from '@/models/explorer.ts';
import { replaceAll } from '@/lib/common.ts'; import { isDefined, replaceAll } from '@/lib/common.ts';
import { import {
getUtcOffsetByUtcOffsetMinutes, getUtcOffsetByUtcOffsetMinutes,