V

Vuer-Viz

vuer-vizv1.0.2

Publication-Quality Visualizations

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.

Basic Line Chart with Columnar Data00.20.40.60.8100.20.40.60.81MonthSales ($k)
Sales
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.

Bar Chart with Columnar Data00.20.40.60.81Q1Q2Q3Q4QuarterRevenue ($M)
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',
  },
];
Multiple Series with Columnar Data00.20.40.60.8100.20.40.60.81MonthAmount ($k)
Revenue
Expenses
Profit
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.

Forecast with Uncertainty Bands00.20.40.60.8100.20.40.60.81TimeValue
Forecast
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');
  }
}

← Back to Guides