بهینه سازی عملکرد نمودار خط با الگوریتم LTTB

هنگام کار با مجموعه داده های بزرگ ، ارائه تمام نقاط در یک نمودار خط می تواند باعث ایجاد مشکلات قابل توجهی شود. به عنوان مثال ، ترسیم 50،000 نقطه داده به طور مستقیم می تواند مرورگر را تحت الشعاع قرار داده و نمودار را پاسخگو نباشد. ابزارهایی مانند AmCharts و Apexcharts با چنین مجموعه داده هایی می جنگند ، در حالی که Echarts عملکرد بهتری دارد اما هنوز برای مجموعه داده های بسیار بزرگ بهینه نشده است.
برای پرداختن به این موضوع ، من Echarts را با بزرگترین الگوریتم مثلث سه سطل (LTTB) ترکیب کردم. LTTB یک الگوریتم کاهش دهنده است که ضمن حفظ ساختار بصری مجموعه داده ، نقاط داده را کاهش می دهد.
در این پست ، من توضیح خواهم داد که چگونه الگوریتم LTTB گام به گام کار می کند و نمونه ای از کاهش 10 امتیاز به 4 است. برای هر مرحله ، کد مربوطه ، توضیحات و تحولات داده حاصل را درج می کنم.
کد الگوریتم LTTB
در اینجا کد کامل الگوریتم LTTB وجود دارد:
export function lttb(data: Point[], threshold: number): Point[] {
const dataLength = data.length;
if (threshold >= dataLength || threshold === 0) {
return data; // No downsampling needed
}
const sampled: Point[] = [];
let sampledIndex = 0;
const bucketSize = (dataLength - 2) / (threshold - 2);
let a = 0; // Start point
let nextA = 0;
sampled[sampledIndex++] = data[a]; // Add the first point
for (let i = 0; i < threshold - 2; i++) {
let avgX = 0;
let avgY = 0;
let avgRangeStart = Math.floor((i + 1) * bucketSize) + 1;
let avgRangeEnd = Math.floor((i + 2) * bucketSize) + 1;
avgRangeEnd = avgRangeEnd < dataLength ? avgRangeEnd : dataLength;
const avgRangeLength = avgRangeEnd - avgRangeStart;
for (; avgRangeStart < avgRangeEnd; avgRangeStart++) {
avgX += data[avgRangeStart].x;
avgY += data[avgRangeStart].y;
}
avgX /= avgRangeLength;
avgY /= avgRangeLength;
let rangeOffs = Math.floor((i + 0) * bucketSize) + 1;
const rangeTo = Math.floor((i + 1) * bucketSize) + 1;
const pointAX = data[a].x;
const pointAY = data[a].y;
let maxArea = -1;
for (; rangeOffs < rangeTo; rangeOffs++) {
const area = Math.abs(
(pointAX - avgX) * (data[rangeOffs].y - pointAY) -
(pointAX - data[rangeOffs].x) * (avgY - pointAY)
);
if (area > maxArea) {
maxArea = area;
nextA = rangeOffs;
}
}
sampled[sampledIndex++] = data[nextA]; // Add the most important point
a = nextA;
}
sampled[sampledIndex++] = data[dataLength - 1]; // Add the last point
return sampled;
}
مثال: کاهش 10 امتیاز به 4 امتیاز
ما با استفاده از الگوریتم LTTB ، مجموعه داده های زیر 10 امتیاز را به 4 امتیاز کاهش خواهیم داد:
مجموعه داده اصلی
const data = [
{ x: 1, y: 10 },
{ x: 2, y: 20 },
{ x: 3, y: 15 },
{ x: 4, y: 25 },
{ x: 5, y: 30 },
{ x: 6, y: 20 },
{ x: 7, y: 40 },
{ x: 8, y: 35 },
{ x: 9, y: 45 },
{ x: 10, y: 50 }
];
const threshold = 4; // Reduce to 4 points
مرحله 1: نکته اول را اضافه کنید
sampled[sampledIndex++] = data[a]; // Add the first point
✅ الگوریتم همیشه با اضافه کردن اولین نقطه داده شروع می شود. این تضمین می کند که مجموعه داده های downsampled به درستی شروع می شود.
📊 نتیجه داده شده:
sampled = [
{ x: 1, y: 10 }
];
مرحله 2: اندازه سطل را محاسبه کنید
const bucketSize = (dataLength - 2) / (threshold - 2);
size اندازه سطل تعیین می کند که چه تعداد نقطه داده در هر “سطل” قرار می گیرد. در این مورد:
بنابراین ، هر سطل حاوی 4 نقطه داده است.
مرحله 3: سطل اول (نقاط 2-5)
let avgX = 0;
let avgY = 0;
let avgRangeStart = Math.floor((i + 1) * bucketSize) + 1;
let avgRangeEnd = Math.floor((i + 2) * bucketSize) + 1;
avgRangeEnd = avgRangeEnd < dataLength ? avgRangeEnd : dataLength;
const avgRangeLength = avgRangeEnd - avgRangeStart;
for (; avgRangeStart < avgRangeEnd; avgRangeStart++) {
avgX += data[avgRangeStart].x;
avgY += data[avgRangeStart].y;
}
avgX /= avgRangeLength;
avgY /= avgRangeLength;
✅ الگوریتم میانگین نقطه اول سطل را محاسبه می کند (امتیاز 2-5). این کار با جمع بندی آنها انجام می شود x
وت y
ارزش ها و تقسیم بر تعداد امتیاز.
مرحله 4: مهمترین نکته را پیدا کنید
let rangeOffs = Math.floor((i + 0) * bucketSize) + 1;
const rangeTo = Math.floor((i + 1) * bucketSize) + 1;
const pointAX = data[a].x;
const pointAY = data[a].y;
let maxArea = -1;
for (; rangeOffs < rangeTo; rangeOffs++) {
const area = Math.abs(
(pointAX - avgX) * (data[rangeOffs].y - pointAY) -
(pointAX - data[rangeOffs].x) * (avgY - pointAY)
);
if (area > maxArea) {
maxArea = area;
nextA = rangeOffs;
}
}
✅ الگوریتم نقطه ای را در سطل مشخص می کند که بزرگترین مثلث را با میانگین نقطه و نقطه قبلاً انتخاب شده تشکیل می دهد.
برای این سطل ، (2،20) بزرگترین منطقه را دارد
sampled = [
{ x: 1, y: 10 },
{ x: 2, y: 20 }
];
مرحله 5: سطل دوم (نقاط 6-9)
این الگوریتم فرآیند را برای سطل دوم (نقاط 6-9) تکرار می کند ، محاسبات:
مهمترین نکته در این سطل (6 ، 20) است.
داده های به روز شده:
sampled = [
{ x: 1, y: 10 },
{ x: 2, y: 20 },
{ x: 6, y: 20 }
];
مرحله ششم: آخرین نکته را اضافه کنید
sampled[sampledIndex++] = data[dataLength - 1]; // Add the last point
✅ الگوریتم همیشه با اضافه کردن آخرین نقطه به پایان می رسد.
📊 داده های نهایی:
sampled = [
{ x: 1, y: 10 },
{ x: 2, y: 20 },
{ x: 6, y: 20 },
{ x: 10, y: 50 }
];
پایان
با ترکیب Echarts با الگوریتم LTTB ، من توانستم مجموعه داده های خود را به طور کارآمد کاهش دهم و عملکرد نمودار را به میزان قابل توجهی بهبود دهم. اگر با مجموعه داده های بزرگ کار می کنید ، این روش راهی عالی برای تعادل عملکرد و دقت بصری است. به من اطلاع دهید که آیا این رویکرد را امتحان کرده اید یا تکنیک های دیگری برای اشتراک گذاری دارید!