ziyuzhou02 commited on
Commit
eda9bad
·
verified ·
1 Parent(s): 347de80

Deploy Space: update src

Browse files
src/__pycache__/about.cpython-311.pyc ADDED
Binary file (1.81 kB). View file
 
src/__pycache__/benchmark_config.cpython-311.pyc ADDED
Binary file (998 Bytes). View file
 
src/__pycache__/eval_schedule.cpython-311.pyc ADDED
Binary file (3.37 kB). View file
 
src/__pycache__/populate.cpython-311.pyc CHANGED
Binary files a/src/__pycache__/populate.cpython-311.pyc and b/src/__pycache__/populate.cpython-311.pyc differ
 
src/__pycache__/utils.cpython-311.pyc CHANGED
Binary files a/src/__pycache__/utils.cpython-311.pyc and b/src/__pycache__/utils.cpython-311.pyc differ
 
src/about.py CHANGED
@@ -39,6 +39,6 @@ CITATION_BUTTON_TEXT = r"""
39
  title={TSFM Realworld Bench: A Benchmark for Time Series Foundation Models},
40
  author={TSFM Realworld Bench Team},
41
  year={2026},
42
- howpublished={\url{https://huggingface.co/spaces/ziyuzhou02/TSFM-Realworld-Bench}}
43
  }
44
  """
 
39
  title={TSFM Realworld Bench: A Benchmark for Time Series Foundation Models},
40
  author={TSFM Realworld Bench Team},
41
  year={2026},
42
+ howpublished={\url{https://huggingface.co/spaces/CityMindDev/TSFM-Realworld-Bench}}
43
  }
44
  """
src/display/__pycache__/css_html_js.cpython-311.pyc ADDED
Binary file (440 Bytes). View file
 
src/display/__pycache__/formatting.cpython-311.pyc CHANGED
Binary files a/src/display/__pycache__/formatting.cpython-311.pyc and b/src/display/__pycache__/formatting.cpython-311.pyc differ
 
src/utils.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import os
2
  import re
3
  from pathlib import Path
@@ -290,12 +291,30 @@ def dataset_short_name(dataset: str) -> str:
290
 
291
 
292
  def lookup_dataset_domain(dataset: str, root_dir: str = "results") -> str:
 
293
  props_path = Path(root_dir) / "dataset_properties.csv"
294
  if props_path.exists():
295
  props = pd.read_csv(props_path)
296
  match = props[props["dataset"].str.lower() == dataset.lower()]
297
  if not match.empty and "domain" in match.columns:
298
  return str(match.iloc[0]["domain"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  return "Other"
300
 
301
 
@@ -341,3 +360,224 @@ def dataset_section_title(dataset: str, root_dir: str, *, ranks: bool = False) -
341
  if ranks:
342
  return f"**{label} · ranks (lower is better)**"
343
  return f"**{label} · metric values (absolute, lower is better)**"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
  import os
3
  import re
4
  from pathlib import Path
 
291
 
292
 
293
  def lookup_dataset_domain(dataset: str, root_dir: str = "results") -> str:
294
+ # Primary: dataset_properties.csv
295
  props_path = Path(root_dir) / "dataset_properties.csv"
296
  if props_path.exists():
297
  props = pd.read_csv(props_path)
298
  match = props[props["dataset"].str.lower() == dataset.lower()]
299
  if not match.empty and "domain" in match.columns:
300
  return str(match.iloc[0]["domain"])
301
+
302
+ # Fallback: read domain column from any all_results.csv
303
+ for subdir in Path(root_dir).iterdir():
304
+ csv_path = subdir / "all_results.csv"
305
+ if not csv_path.exists():
306
+ continue
307
+ try:
308
+ df = pd.read_csv(csv_path)
309
+ if "domain" not in df.columns or "dataset" not in df.columns:
310
+ continue
311
+ # dataset column in all_results is "dataset_id/freq/term", strip suffix
312
+ df["_ds_key"] = df["dataset"].astype(str).str.split("/").str[0]
313
+ match = df[df["_ds_key"].str.lower() == dataset.lower()]
314
+ if not match.empty:
315
+ return str(match.iloc[0]["domain"])
316
+ except Exception:
317
+ continue
318
  return "Other"
319
 
320
 
 
360
  if ranks:
361
  return f"**{label} · ranks (lower is better)**"
362
  return f"**{label} · metric values (absolute, lower is better)**"
363
+
364
+
365
+ BASELINE_RANK_HISTORY_COLUMNS = [
366
+ "date",
367
+ "model",
368
+ "MASE_Rank",
369
+ "CRPS_Rank",
370
+ "MAE_Rank",
371
+ "RMSE_Rank",
372
+ "sMAPE_Rank",
373
+ "MSIS_Rank",
374
+ "ND_Rank",
375
+ "NRMSE_Rank",
376
+ "MAPE_Rank",
377
+ ]
378
+
379
+ _RANK_COLS = [c for c in BASELINE_RANK_HISTORY_COLUMNS if c.endswith("_Rank")]
380
+ _DAILY_DISPLAY_COLS = ["date"] + _RANK_COLS
381
+ _WEEKLY_DISPLAY_COLS = ["week"] + _RANK_COLS
382
+
383
+
384
+ def _read_rank_history(path: Path, date_col: str) -> pd.DataFrame:
385
+ """Read a rank history CSV, coerce rank columns to float, sort by date_col."""
386
+ if not path.exists():
387
+ return pd.DataFrame(columns=[date_col] + _RANK_COLS)
388
+ try:
389
+ df = pd.read_csv(path)
390
+ except Exception:
391
+ return pd.DataFrame(columns=[date_col] + _RANK_COLS)
392
+ for col in _RANK_COLS:
393
+ if col in df.columns:
394
+ df[col] = pd.to_numeric(df[col], errors="coerce").round(2)
395
+ if date_col in df.columns:
396
+ df = df.sort_values(date_col)
397
+ present = [c for c in [date_col] + _RANK_COLS if c in df.columns]
398
+ return df[present].reset_index(drop=True)
399
+
400
+
401
+ def load_baseline_rank_history(root_dir: str = "results") -> pd.DataFrame:
402
+ """Load daily baseline rank history (baseline_rank_history_daily.csv)."""
403
+ # Also try the legacy single-file name for backward-compat
404
+ for name in ("baseline_rank_history_daily.csv", "baseline_rank_history.csv"):
405
+ path = Path(root_dir) / name
406
+ if path.exists():
407
+ return _read_rank_history(path, "date")
408
+ return pd.DataFrame(columns=_DAILY_DISPLAY_COLS)
409
+
410
+
411
+ def load_baseline_rank_history_weekly(root_dir: str = "results") -> pd.DataFrame:
412
+ """Load weekly aggregated baseline rank history (baseline_rank_history_weekly.csv)."""
413
+ return _read_rank_history(Path(root_dir) / "baseline_rank_history_weekly.csv", "week")
414
+
415
+
416
+ # ---------------------------------------------------------------------------
417
+ # Forecast snapshot utilities
418
+ # ---------------------------------------------------------------------------
419
+
420
+ def load_forecast_snapshots(root_dir: str, model_slug: str) -> dict[str, dict]:
421
+ """Load all forecast JSON files for a given model slug.
422
+
423
+ Returns a dict keyed by the dataset display name (from snapshot["dataset"]).
424
+ """
425
+ forecasts_dir = Path(root_dir) / model_slug / "forecasts"
426
+ snapshots: dict[str, dict] = {}
427
+ if not forecasts_dir.exists():
428
+ return snapshots
429
+ for fp in sorted(forecasts_dir.glob("*.json")):
430
+ try:
431
+ snap = json.loads(fp.read_text())
432
+ snapshots[snap.get("dataset", fp.stem)] = snap
433
+ except Exception:
434
+ pass
435
+ return snapshots
436
+
437
+
438
+ def _get_plotly():
439
+ """Lazy import plotly to avoid import errors in non-chart contexts."""
440
+ try:
441
+ import plotly.graph_objects as go
442
+ return go
443
+ except ImportError:
444
+ return None
445
+
446
+
447
+ def make_forecast_plot(snapshot: dict | None) -> object | None:
448
+ """Build a plotly Figure showing context + actuals vs p50 forecast with p10/p90 band.
449
+
450
+ Returns None if snapshot is None or plotly is unavailable.
451
+ """
452
+ go = _get_plotly()
453
+ if go is None or snapshot is None:
454
+ return None
455
+
456
+ ctx = snapshot.get("context", [])
457
+ actuals = snapshot.get("actuals", [])
458
+ p50 = snapshot.get("p50", [])
459
+ p10 = snapshot.get("p10", [])
460
+ p90 = snapshot.get("p90", [])
461
+ n_ctx = len(ctx)
462
+ n_fut = len(actuals)
463
+
464
+ ctx_x = list(range(n_ctx))
465
+ fut_x = list(range(n_ctx, n_ctx + n_fut))
466
+
467
+ fig = go.Figure()
468
+
469
+ # Context (historical)
470
+ fig.add_trace(go.Scatter(
471
+ x=ctx_x, y=ctx,
472
+ mode="lines",
473
+ name="Context (history)",
474
+ line=dict(color="#94a3b8", width=1.5),
475
+ ))
476
+
477
+ # Actuals (ground truth for forecast horizon)
478
+ fig.add_trace(go.Scatter(
479
+ x=fut_x, y=actuals,
480
+ mode="lines",
481
+ name="Actual",
482
+ line=dict(color="#3b82f6", width=2),
483
+ ))
484
+
485
+ # p10-p90 confidence band
486
+ if p10 and p90 and len(p10) == n_fut and len(p90) == n_fut:
487
+ fig.add_trace(go.Scatter(
488
+ x=fut_x + fut_x[::-1],
489
+ y=p90 + p10[::-1],
490
+ fill="toself",
491
+ fillcolor="rgba(239,68,68,0.15)",
492
+ line=dict(color="rgba(255,255,255,0)"),
493
+ name="p10-p90 band",
494
+ showlegend=True,
495
+ ))
496
+
497
+ # p50 forecast
498
+ if p50 and len(p50) == n_fut:
499
+ fig.add_trace(go.Scatter(
500
+ x=fut_x, y=p50,
501
+ mode="lines",
502
+ name="Forecast (p50)",
503
+ line=dict(color="#ef4444", width=2, dash="dash"),
504
+ ))
505
+
506
+ dataset_label = snapshot.get("dataset", "").split("/")[0]
507
+ model_label = snapshot.get("model", "")
508
+ evaluated_at = snapshot.get("evaluated_at", "")[:10]
509
+ fig.update_layout(
510
+ title=dict(
511
+ text=f"{dataset_label} — {model_label} ({evaluated_at})",
512
+ font=dict(size=14),
513
+ ),
514
+ xaxis_title="Time step",
515
+ yaxis_title="Value",
516
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
517
+ margin=dict(l=40, r=20, t=60, b=40),
518
+ height=350,
519
+ paper_bgcolor="rgba(0,0,0,0)",
520
+ plot_bgcolor="rgba(0,0,0,0)",
521
+ font=dict(color="#e2e8f0"),
522
+ xaxis=dict(gridcolor="rgba(255,255,255,0.08)"),
523
+ yaxis=dict(gridcolor="rgba(255,255,255,0.08)"),
524
+ )
525
+ # Vertical line separating context from forecast
526
+ fig.add_vline(
527
+ x=n_ctx - 0.5,
528
+ line_dash="dot",
529
+ line_color="rgba(255,255,255,0.3)",
530
+ annotation_text="forecast start",
531
+ annotation_font_color="#94a3b8",
532
+ )
533
+ return fig
534
+
535
+
536
+ def make_rank_trend_plot(history_df: pd.DataFrame, metrics: list[str] | None = None) -> object | None:
537
+ """Build a plotly Figure showing selected rank metrics over time (date on X axis).
538
+
539
+ Args:
540
+ history_df: DataFrame from load_baseline_rank_history() with a 'date' column.
541
+ metrics: List of rank column names to plot. Defaults to MASE_Rank and CRPS_Rank.
542
+
543
+ Returns None if fewer than 1 data point or plotly unavailable.
544
+ """
545
+ go = _get_plotly()
546
+ if go is None or history_df is None or history_df.empty:
547
+ return None
548
+
549
+ if metrics is None:
550
+ metrics = ["MASE_Rank", "CRPS_Rank", "MAE_Rank", "RMSE_Rank"]
551
+
552
+ date_col = "date" if "date" in history_df.columns else "week"
553
+ present_metrics = [m for m in metrics if m in history_df.columns]
554
+ if not present_metrics:
555
+ return None
556
+
557
+ COLORS = ["#f97316", "#a78bfa", "#34d399", "#60a5fa", "#fb7185", "#fbbf24"]
558
+
559
+ fig = go.Figure()
560
+ for i, col in enumerate(present_metrics):
561
+ fig.add_trace(go.Scatter(
562
+ x=history_df[date_col],
563
+ y=history_df[col],
564
+ mode="lines+markers",
565
+ name=col.replace("_Rank", ""),
566
+ line=dict(color=COLORS[i % len(COLORS)], width=2),
567
+ marker=dict(size=6),
568
+ ))
569
+
570
+ fig.update_layout(
571
+ title=dict(text="Baseline rank trend over time (lower is better)", font=dict(size=14)),
572
+ xaxis_title=date_col.capitalize(),
573
+ yaxis_title="Average rank",
574
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
575
+ margin=dict(l=40, r=20, t=60, b=40),
576
+ height=320,
577
+ paper_bgcolor="rgba(0,0,0,0)",
578
+ plot_bgcolor="rgba(0,0,0,0)",
579
+ font=dict(color="#e2e8f0"),
580
+ xaxis=dict(gridcolor="rgba(255,255,255,0.08)"),
581
+ yaxis=dict(gridcolor="rgba(255,255,255,0.08)"),
582
+ )
583
+ return fig