diff --git a/frontend/src/app/components/statistics/statistics.component.scss b/frontend/src/app/components/statistics/statistics.component.scss
index d883e48c3..3d4813fb5 100644
--- a/frontend/src/app/components/statistics/statistics.component.scss
+++ b/frontend/src/app/components/statistics/statistics.component.scss
@@ -222,4 +222,13 @@
border-top-right-radius: 0;
}
}
+}
+
+.vbytes-title {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ @media (max-width: 767px) {
+ display: block;
+ }
}
\ No newline at end of file
diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts
index 6bc58b6d7..e80ab83ad 100644
--- a/frontend/src/app/components/statistics/statistics.component.ts
+++ b/frontend/src/app/components/statistics/statistics.component.ts
@@ -35,7 +35,7 @@ export class StatisticsComponent implements OnInit {
showCount = false;
maxFeeIndex: number;
dropDownOpen = false;
-
+ outlierCappingEnabled = false;
mempoolStats: OptimizedMempoolStats[] = [];
mempoolVsizeFeesData: any;
@@ -156,12 +156,14 @@ export class StatisticsComponent implements OnInit {
}
this.maxFeeIndex = maxTier;
- this.capExtremeVbytesValues();
-
this.mempoolTransactionsWeightPerSecondData = {
labels: labels,
series: [mempoolStats.map((stats) => [stats.added * 1000, stats.vbytes_per_second])],
};
+
+ if (this.outlierCappingEnabled) {
+ this.capExtremeVbytesValues();
+ }
}
saveGraphPreference() {
@@ -211,24 +213,25 @@ export class StatisticsComponent implements OnInit {
}
});
}
-
+
/**
* All value higher that "median * capRatio" are capped
*/
+ onOutlierToggleChange(e) {
+ this.outlierCappingEnabled = e.target.checked;
+ this.handleNewMempoolData(this.mempoolStats);
+ }
capExtremeVbytesValues() {
if (this.stateService.network.length !== 0) {
return; // Only cap on Bitcoin mainnet
}
- let capRatio = 10;
- if (['1m', '3m', '6m', '1y', '2y', '3y', '4y'].includes(this.graphWindowPreference)) {
- capRatio = 4;
- }
+ let capRatio = 4;
// Find median value
const vBytes: number[] = [];
- for (const stat of this.mempoolStats) {
- vBytes.push(stat.vbytes_per_second);
+ for (const stat of this.mempoolTransactionsWeightPerSecondData.series[0]) {
+ vBytes.push(stat[1]);
}
const sorted = vBytes.slice().sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
@@ -238,8 +241,8 @@ export class StatisticsComponent implements OnInit {
}
// Cap
- for (const stat of this.mempoolStats) {
- stat.vbytes_per_second = Math.min(median * capRatio, stat.vbytes_per_second);
+ for (const stat of this.mempoolTransactionsWeightPerSecondData.series[0]) {
+ stat[1] = Math.min(median * capRatio, stat[1]);
}
}
From 2d30c0b588038caec287cac5faa96e8122f81e67 Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 15 Nov 2023 14:08:44 +0900
Subject: [PATCH 2/4] [graph] use echart echart yaxis `max` property instead of
modifying the data itself
---
.../incoming-transactions-graph.component.ts | 26 +++++++++++++-
.../statistics/statistics.component.html | 2 +-
.../statistics/statistics.component.ts | 34 +------------------
3 files changed, 27 insertions(+), 35 deletions(-)
diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts
index 667ad1e28..82e3e77e7 100644
--- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts
+++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts
@@ -7,6 +7,8 @@ import { formatNumber } from '@angular/common';
import { StateService } from '../../services/state.service';
import { Subscription } from 'rxjs';
+const OUTLIERS_MEDIAN_MULTIPLIER = 4;
+
@Component({
selector: 'app-incoming-transactions-graph',
templateUrl: './incoming-transactions-graph.component.html',
@@ -29,6 +31,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
@Input() left: number | string = '0';
@Input() template: ('widget' | 'advanced') = 'widget';
@Input() windowPreferenceOverride: string;
+ @Input() outlierCappingEnabled: boolean = false;
isLoading = true;
mempoolStatsChartOption: EChartsOption = {};
@@ -40,6 +43,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
MA: number[][] = [];
weightMode: boolean = false;
rateUnitSub: Subscription;
+ medianVbytesPerSecond: number | undefined;
constructor(
@Inject(LOCALE_ID) private locale: string,
@@ -65,16 +69,35 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
const windowSize = Math.max(10, Math.floor(this.data.series[0].length / 8));
this.MA = this.calculateMA(this.data.series[0], windowSize);
+ if (this.outlierCappingEnabled === true) {
+ this.computeMedianVbytesPerSecond(this.data.series[0]);
+ }
this.mountChart();
}
rendered() {
if (!this.data) {
- return;
+ return;
}
this.isLoading = false;
}
+ /**
+ * Calculate the median value of the vbytes per second chart to hide outliers
+ */
+ computeMedianVbytesPerSecond(data: number[][]): void {
+ const vBytes: number[] = [];
+ for (const value of data) {
+ vBytes.push(value[1]);
+ }
+ const sorted = vBytes.slice().sort((a, b) => a - b);
+ const middle = Math.floor(sorted.length / 2);
+ this.medianVbytesPerSecond = sorted[middle];
+ if (sorted.length % 2 === 0) {
+ this.medianVbytesPerSecond = (sorted[middle - 1] + sorted[middle]) / 2;
+ }
+ }
+
/// calculate the moving average of the provided data based on windowSize
calculateMA(data: number[][], windowSize: number = 100): number[][] {
//update const variables that are not changed
@@ -232,6 +255,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
}
],
yAxis: {
+ max: this.outlierCappingEnabled ? Math.round(this.medianVbytesPerSecond * OUTLIERS_MEDIAN_MULTIPLIER) : undefined,
type: 'value',
axisLabel: {
fontSize: 11,
diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html
index 3f0ea61f5..c726e354e 100644
--- a/frontend/src/app/components/statistics/statistics.component.html
+++ b/frontend/src/app/components/statistics/statistics.component.html
@@ -128,7 +128,7 @@
+ [data]="mempoolTransactionsWeightPerSecondData" [outlierCappingEnabled]="outlierCappingEnabled">
diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts
index e80ab83ad..8e01e068b 100644
--- a/frontend/src/app/components/statistics/statistics.component.ts
+++ b/frontend/src/app/components/statistics/statistics.component.ts
@@ -160,10 +160,6 @@ export class StatisticsComponent implements OnInit {
labels: labels,
series: [mempoolStats.map((stats) => [stats.added * 1000, stats.vbytes_per_second])],
};
-
- if (this.outlierCappingEnabled) {
- this.capExtremeVbytesValues();
- }
}
saveGraphPreference() {
@@ -214,36 +210,8 @@ export class StatisticsComponent implements OnInit {
});
}
- /**
- * All value higher that "median * capRatio" are capped
- */
- onOutlierToggleChange(e) {
+ onOutlierToggleChange(e): void {
this.outlierCappingEnabled = e.target.checked;
- this.handleNewMempoolData(this.mempoolStats);
- }
- capExtremeVbytesValues() {
- if (this.stateService.network.length !== 0) {
- return; // Only cap on Bitcoin mainnet
- }
-
- let capRatio = 4;
-
- // Find median value
- const vBytes: number[] = [];
- for (const stat of this.mempoolTransactionsWeightPerSecondData.series[0]) {
- vBytes.push(stat[1]);
- }
- const sorted = vBytes.slice().sort((a, b) => a - b);
- const middle = Math.floor(sorted.length / 2);
- let median = sorted[middle];
- if (sorted.length % 2 === 0) {
- median = (sorted[middle - 1] + sorted[middle]) / 2;
- }
-
- // Cap
- for (const stat of this.mempoolTransactionsWeightPerSecondData.series[0]) {
- stat[1] = Math.min(median * capRatio, stat[1]);
- }
}
onSaveChart(name) {
From dc26c6f105c666cbce9aa1248d95ce850f3558fb Mon Sep 17 00:00:00 2001
From: nymkappa <1612910616@pm.me>
Date: Wed, 15 Nov 2023 18:46:33 +0900
Subject: [PATCH 3/4] [graph] don't change yaxis scale if no outliers - save
state in localstorage
---
.../incoming-transactions-graph.component.ts | 10 +++++++++-
.../components/statistics/statistics.component.html | 2 +-
.../app/components/statistics/statistics.component.ts | 2 ++
3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts
index 82e3e77e7..f6d2de5df 100644
--- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts
+++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts
@@ -255,7 +255,15 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
}
],
yAxis: {
- max: this.outlierCappingEnabled ? Math.round(this.medianVbytesPerSecond * OUTLIERS_MEDIAN_MULTIPLIER) : undefined,
+ max: (value) => {
+ if (!this.outlierCappingEnabled) {
+ return undefined;
+ }
+ if (value.max < this.medianVbytesPerSecond * OUTLIERS_MEDIAN_MULTIPLIER) {
+ return undefined;
+ }
+ return Math.round(this.medianVbytesPerSecond * OUTLIERS_MEDIAN_MULTIPLIER);
+ },
type: 'value',
axisLabel: {
fontSize: 11,
diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html
index c726e354e..0bb10a1c3 100644
--- a/frontend/src/app/components/statistics/statistics.component.html
+++ b/frontend/src/app/components/statistics/statistics.component.html
@@ -117,7 +117,7 @@