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 -
yKeysarray
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 }; }
Related Documentation
- Data Hooks Overview - Data providers and hook system
- Data Query Examples - Query patterns and examples
- Data Structure & Format Guide - Columnar data format specification