V

Vuer-Viz

vuer-vizv1.0.2

Publication-Quality Visualizations

useSeries

Fetches time series data with support for aggregation across multiple runs. The useSeries hook handles both single experiment data and aggregation across multiple runs. It is designed for line charts and other time-series visualizations.

Example Data: Learning Curves

The documentation includes realistic training curves comparing FFN vs MLP architectures.

export interface ExperimentRun {
  prefix: string;
  metrics: {
    step: number[];
    reward: number[];
    loss: number[];
    "eval.reward": number[];
    "eval.loss": number[];
  };
}

export const learningCurves: Record<string, ExperimentRun[]> = {
  "ffn/no-tgt": [...],    // Fast learning, plateaus ~850
  "mlp/3_layer": [...],   // Slower learning, better final ~900
};
  • 200 steps (0-199k)
  • 3 seeds per method
  • FFN learns faster initially but plateaus lower
  • MLP has slower initial learning but achieves better final performance

Type Interface

export type SortOrder = "asc" | "desc" | "natural" | "mtime" | "name";

export type Aggregation =
            | "mean" | "median" | "std" | "var" | "min" | "max" | "sum" | "count"
            | "Q05" | "Q10" | "Q25" | "Q50" | "Q75" | "Q90" | "Q95"
            | null;

export interface SeriesQuery {
  prefix?: string;              // Experiment directory prefix (used as current working directory for glob)
  xKey?: string;                // X-axis key (dot-separated path in the log data)
  yKey?: string;                // Single Y-axis key
  yKeys?: string[];             // Multiple Y-axis keys (alternative to yKey)
  xMin?: number; xMax?: number; // determines the x-range for the data query
  yMin?: number; yMax?: number; // determines the y-limit, for presentation only.
}

export interface AggregatedSeriesQuery extends SeriesQuery {
  /** Glob pattern for matching multiple experiment directories */
  glob?: string;
  /** Aggregation method for combining data across matched experiments */
  agg?: Aggregation;
  /** Sort order for selecting experiments */
  sortOrder?: SortOrder;
}

Reference Implementation

1. Define the fetch function

The fetch function handles the data retrieval logic:

async function fetchSeries(query: AggregatedSeriesQuery): Promise<LineSeries[]> {
  const response = await fetch("/api/series", {
    method: "POST",
    body: JSON.stringify(query),
  });
  return await response.json();
}

2. Pass to SeriesDataProvider

The provider builds the internal useSeries hook using your fetch function:

<SeriesDataProvider fetchSeries={fetchSeries}>
  <YourCharts />
</SeriesDataProvider>

3. Internal hook implementation

The provider internally creates the useSeries hook with loading and error states:

// Inside SeriesDataProvider
function useSeries(query: AggregatedSeriesQuery) {
  const [series, setSeries] = useState<LineSeries[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function load() {
      try {
        setLoading(true);
        setError(null);
        const data = await fetchSeries(query);
        if (!cancelled) setSeries(data);
      } catch (err) {
        if (!cancelled) setError(err as Error);
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    load();
    return () => { cancelled = true; };
  }, [query]);

  return { series, loading, error };
}

Key Features

  • Glob pattern matching - "ffn/*", "*/seed-0"
  • Single experiments - agg=null
  • Multi-run aggregation - agg="mean"|"median"|"std"|...
  • Multiple metrics - yKeys array

Aggregation Methods

  • null - Return all individual series
  • "mean" - Average across runs
  • "median" - Median across runs
  • "std" - Standard deviation
  • "var" - Variance
  • "min" / "max" - Min/max values
  • "sum" / "count" - Sum/count
  • "Q25", "Q75", etc. - Quantiles

Usage Examples

Single Experiment

const { series } = useSeries({
  prefix: "ffn/no-tgt/seed-0",
  xKey: "step",
  yKeys: ["reward", "eval.reward"]
});

Aggregated Across Seeds

const { series } = useSeries({
  glob: "ffn/*",
  xKey: "step",
  yKeys: ["reward", "eval.reward"],
  agg: "mean"
});

Multiple Methods Comparison

const { series: ffnSeries } = useSeries({
  glob: "ffn/*",
  xKey: "step",
  yKey: "reward",
  agg: "mean"
});

const { series: mlpSeries } = useSeries({
  glob: "mlp/*",
  xKey: "step",
  yKey: "reward",
  agg: "mean"
});

Alternative Backend: File System (Node.js/Electron)

export function useSeries(query: AggregatedSeriesQuery) {
  const series = useMemo(() => {
    const fs = require('fs');
    const path = require('path');

    // Load experiment logs
    const logPath = path.join(query.prefix, 'metrics.jsonl');
    const logs = fs.readFileSync(logPath, 'utf-8')
      .split('\n')
      .filter(Boolean)
      .map(line => JSON.parse(line));

    // Transform to columnar format
    return extractTimeSeries(logs, query);
  }, [query]);

  return { series, loading: false, error: null };
}