Data Structure and Format
Vuer-Viz uses a columnar data format where each dimension (x, y, high, low) is stored as a separate array rather than an array of point objects.
// Columnar format (Vuer-Viz) const series = { label: "Sales", x: [0, 1, 2, 3, 4], y: [10, 25, 18, 32, 28], };
Why Columnar Format?
Storing each dimension as a separate array of primitives allows JavaScript engines to optimize memory layout and iteration. Arrays of homogeneous types are CPU cache-friendly, enabling better vectorization and reducing garbage collection pressure. This format also matches pandas/numpy conventions, making it natural for data scientists working across Python and JavaScript pipelines.
-
Lower Memory Usage: Columnar format reduces memory usage significantly for large datasets:
// Array of objects: ~48 bytes per point const objectFormat = Array(10000).fill(null).map((_, i) => ({ x: i, y: i * 2 })); // Columnar format: ~16 bytes per point const x = Array(10000).fill(null).map((_, i) => i); const y = Array(10000).fill(null).map((_, i) => i * 2); -
2-3x Faster Iteration Speed: Columnar format iterates ~2-3x faster for rendering:
// Fast: Direct array access for (let i = 0; i < x.length; i++) { const px = x[i]; const py = y[i]; // Process point... } // Slower: Object destructuring for (const point of points) { const { x: px, y: py } = point; // Process point... }
Line Charts
Simple time-series data with x and y arrays.
import { LineChart } from '@vuer-ai/vuer-viz';
// Columnar format: each dimension is a separate array
const x = [0, 1, 2, 3, 4, 5];
const y = [10, 25, 18, 32, 28, 35];
export default function BasicLineExample() {
return (
<LineChart
series={[
{
label: 'Sales',
x,
y,
color: '#3498db',
},
]}
title="Basic Line Chart with Columnar Data"
xLabel="Month"
yLabel="Sales ($k)"
width={770}
/>
);
}
Bar Charts
Categories and values as separate arrays.
import { BarChart } from '@vuer-ai/vuer-viz';
// Columnar format for bar charts
const label = ['Q1', 'Q2', 'Q3', 'Q4'];
const value = [45, 62, 58, 71];
export default function BasicBarExample() {
return (
<BarChart
label={label}
value={value}
title="Bar Chart with Columnar Data"
xLabel="Quarter"
yLabel="Revenue ($M)"
width={770}
/>
);
}
Multiple Series
Each series maintains its own columnar data.
const series = [ { label: 'Revenue', x: months, y: revenue, color: '#2ecc71', }, { label: 'Expenses', x: months, y: expenses, color: '#e74c3c', }, ];
import { LineChart, TimeSeries } from "@vuer-ai/vuer-viz";
// Each series has its own columnar data
const months = [0, 1, 2, 3, 4, 5];
const revenue = [45, 52, 48, 61, 58, 67];
const expenses = [30, 35, 38, 42, 45, 48];
const profit = months.map((_, i) => revenue[i] - expenses[i]);
export default function MultipleSeriesExample() {
return (
<LineChart title="Multiple Series with Columnar Data" xLabel="Month" yLabel="Amount ($k)" width={770} legend>
<TimeSeries label="Revenue" x={months} y={revenue} color="#2ecc71" lineWidth={2} />
<TimeSeries label="Expenses" x={months} y={expenses} color="#e74c3c" lineWidth={2} />
<TimeSeries label="Profit" x={months} y={profit} color="#3498db" lineWidth={2} />
</LineChart>
);
}
Working with Uncertainty Bounds
Add high and low arrays for confidence intervals and error bands.
import { LineChart } from '@vuer-ai/vuer-viz';
// Columnar format with uncertainty bounds
const time = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const forecast = [50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70];
// Upper and lower bounds for uncertainty
const high = forecast.map((v, i) => v + (i * 1.5));
const low = forecast.map((v, i) => v - (i * 1.5));
export default function WithUncertaintyExample() {
return (
<LineChart
series={[
{
label: 'Forecast',
x: time,
y: forecast,
high,
low,
color: '#9b59b6',
fillColor: '#9b59b6',
fillOpacity: 0.2,
lineWidth: 2,
dashArray: '5 5',
},
]}
title="Forecast with Uncertainty Bands"
xLabel="Time"
yLabel="Value"
width={770}
/>
);
}
const series = { label: 'Forecast', x: time, y: forecast, high: upperBound, // Array of upper values low: lowerBound, // Array of lower values };
TypeScript Support
BarChart Types
import { type BarSeries } from '@vuer-ai/vuer-viz'; interface BarSeries { label: string; data: number[]; // Values for each category color?: string; }
Best Practices
1. Keep Arrays Aligned
All arrays in a series must have the same length:
// ✅ Good: All arrays same length const series = { label: 'Data', x: [0, 1, 2], y: [10, 20, 15], high: [12, 22, 17], low: [8, 18, 13], }; // ❌ Bad: Mismatched lengths const series = { label: 'Data', x: [0, 1, 2], y: [10, 20], // Missing value! };
2. Use Typed Conversions
Leverage TypeScript for safe data transformations:
interface APIResponse { timestamp: number; value: number; error: number; } function toLineSeries(data: APIResponse[], label: string): LineSeries { return { label, x: data.map(d => d.timestamp), y: data.map(d => d.value), high: data.map(d => d.value + d.error), low: data.map(d => d.value - d.error), }; }
3. Instancing and Reuse the Index Array
Arrays can be shared across series for better performance:
const sharedTime = [0, 1, 2, 3, 4]; // Reused across series const series = [ { label: 'Metric A', x: sharedTime, // Shared reference y: [10, 20, 15, 25, 22], }, { label: 'Metric B', x: sharedTime, // Same array y: [15, 18, 22, 20, 28], }, ];
4. Efficient Updates
For real-time data, update arrays immutably:
// Add new data point const newX = [...x, nextTimestamp]; const newY = [...y, nextValue]; // Or use .concat() for better performance with large arrays const newX = x.concat(nextTimestamp); const newY = y.concat(nextValue); // For sliding windows, use .slice() const windowSize = 100; const newX = [...x, nextTimestamp].slice(-windowSize); const newY = [...y, nextValue].slice(-windowSize);
5. Validate Data
Add simple validation for development:
function validateSeries(series: LineSeries): void { const length = series.x.length; if (series.y.length !== length) { throw new Error('x and y arrays must have same length'); } if (series.high && series.high.length !== length) { throw new Error('high array must match x/y length'); } if (series.low && series.low.length !== length) { throw new Error('low array must match x/y length'); } }