ziyuzhou02 commited on
Commit
6e22424
·
verified ·
1 Parent(s): 0797800

Deploy Space: update src

Browse files
Files changed (3) hide show
  1. src/about.py +6 -4
  2. src/rank_based_metric.py +99 -0
  3. src/utils.py +50 -15
src/about.py CHANGED
@@ -5,8 +5,9 @@ INTRODUCTION_TEXT = """
5
  real-world data** with **zero-shot API inference** via [TSFM.ai](https://tsfm.ai/).
6
  Following [GIFT-Eval](https://huggingface.co/spaces/Salesforce/GIFT-Eval), the leaderboard
7
  reports **absolute metric values** (not normalized to a baseline model) and **per-dataset
8
- ranks**. Use the **Overall** tab for aggregate scores; each subsequent tab is one **dataset
9
- (domain)** with its own absolute results.
 
10
  """
11
 
12
  LLM_BENCHMARKS_TEXT = """
@@ -36,9 +37,10 @@ Once your Pull Request is opened, our automated sandbox pipeline will load your
36
 
37
  ## Metrics
38
 
39
- - **MASE** — Mean Absolute Scaled Error (absolute)
40
  - **CRPS** — Mean Weighted Sum Quantile Loss (probabilistic forecast quality)
41
- - **MASE_Rank** / **CRPS_Rank** per-dataset rank (lower is better)
 
42
  """
43
 
44
  CITATION_BUTTON_LABEL = "Copy citation"
 
5
  real-world data** with **zero-shot API inference** via [TSFM.ai](https://tsfm.ai/).
6
  Following [GIFT-Eval](https://huggingface.co/spaces/Salesforce/GIFT-Eval), the leaderboard
7
  reports **absolute metric values** (not normalized to a baseline model) and **per-dataset
8
+ ranks**. The Overall tab also reports **RankScore**, an Elo-style rank-based aggregate
9
+ computed from per-dataset MSE and CRPS ranks. Use the **Overall** tab for aggregate scores;
10
+ each subsequent tab is one **dataset (domain)** with its own absolute results.
11
  """
12
 
13
  LLM_BENCHMARKS_TEXT = """
 
37
 
38
  ## Metrics
39
 
40
+ - **MSE** — Mean Squared Error on the mean forecast (absolute)
41
  - **CRPS** — Mean Weighted Sum Quantile Loss (probabilistic forecast quality)
42
+ - **RankScore** Elo-style aggregate from per-dataset MSE and CRPS ranks (higher is better)
43
+ - **MSE_Rank** / **CRPS_Rank** — per-dataset rank (lower is better)
44
  """
45
 
46
  CITATION_BUTTON_LABEL = "Copy citation"
src/rank_based_metric.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Dict, Sequence, Tuple
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+
9
+ def expected_score(rating_a: float, rating_b: float) -> float:
10
+ return 1.0 / (1.0 + 10 ** ((rating_b - rating_a) / 400.0))
11
+
12
+
13
+ def update_ratings(rating_a: float, rating_b: float, score_a: float, k_factor: float) -> Tuple[float, float]:
14
+ expected_a = expected_score(rating_a, rating_b)
15
+ expected_b = expected_score(rating_b, rating_a)
16
+ score_b = 1.0 - score_a
17
+ return (
18
+ rating_a + k_factor * (score_a - expected_a),
19
+ rating_b + k_factor * (score_b - expected_b),
20
+ )
21
+
22
+
23
+ def calculate_elo(
24
+ df_long: pd.DataFrame,
25
+ initial_rating: int = 1000,
26
+ k_factor: float = 32,
27
+ tie_epsilon: float = 0.0,
28
+ ) -> Dict[str, float]:
29
+ required = {"dataset", "model", "score"}
30
+ missing = required - set(df_long.columns)
31
+ if missing:
32
+ raise ValueError(f"df_long is missing columns: {sorted(missing)}")
33
+
34
+ clean = df_long.loc[:, ["dataset", "model", "score"]].copy()
35
+ clean["score"] = pd.to_numeric(clean["score"], errors="coerce")
36
+ clean = clean.dropna(subset=["dataset", "model"])
37
+
38
+ models = sorted(clean["model"].astype(str).unique())
39
+ ratings = {model: float(initial_rating) for model in models}
40
+
41
+ for dataset in sorted(clean["dataset"].astype(str).unique()):
42
+ sub = clean[clean["dataset"].astype(str) == dataset].dropna(subset=["score"])
43
+ scores = sub.groupby("model")["score"].mean()
44
+ present_models = sorted(str(model) for model in scores.index)
45
+ for idx, model_a in enumerate(present_models):
46
+ for model_b in present_models[idx + 1 :]:
47
+ score_a_value = float(scores.loc[model_a])
48
+ score_b_value = float(scores.loc[model_b])
49
+ if score_a_value > score_b_value + tie_epsilon:
50
+ match_score_a = 1.0
51
+ elif score_b_value > score_a_value + tie_epsilon:
52
+ match_score_a = 0.0
53
+ else:
54
+ match_score_a = 0.5
55
+
56
+ rating_a, rating_b = update_ratings(
57
+ ratings[model_a],
58
+ ratings[model_b],
59
+ match_score_a,
60
+ k_factor,
61
+ )
62
+ ratings[model_a] = rating_a
63
+ ratings[model_b] = rating_b
64
+
65
+ return ratings
66
+
67
+
68
+ def compute_rank_score(
69
+ df: pd.DataFrame,
70
+ metric_columns: Sequence[str],
71
+ group_columns: Sequence[str] = ("dataset",),
72
+ *,
73
+ initial_rating: int = 1000,
74
+ k_factor: float = 32,
75
+ ) -> pd.Series:
76
+ required = {"model", *group_columns, *metric_columns}
77
+ missing = required - set(df.columns)
78
+ if missing:
79
+ raise ValueError(f"df is missing columns: {sorted(missing)}")
80
+
81
+ work = df.loc[:, ["model", *group_columns, *metric_columns]].copy()
82
+ for metric in metric_columns:
83
+ work[metric] = pd.to_numeric(work[metric], errors="coerce")
84
+ work[f"__rank_{metric}"] = work.groupby(list(group_columns))[metric].rank(
85
+ method="average",
86
+ ascending=True,
87
+ )
88
+
89
+ rank_cols = [f"__rank_{metric}" for metric in metric_columns]
90
+ work["score"] = -work[rank_cols].mean(axis=1, skipna=True)
91
+ work = work.dropna(subset=["score"])
92
+ work["dataset"] = work.loc[:, list(group_columns)].astype(str).agg("::".join, axis=1)
93
+
94
+ ratings = calculate_elo(
95
+ work.loc[:, ["dataset", "model", "score"]],
96
+ initial_rating=initial_rating,
97
+ k_factor=k_factor,
98
+ )
99
+ return pd.Series(ratings, name="RankScore")
src/utils.py CHANGED
@@ -11,6 +11,7 @@ import pandas as pd
11
  from scipy import stats
12
 
13
  from src.display.formatting import format_timestamp_utc8
 
14
 
15
  METRIC_COLUMNS = [
16
  "eval_metrics/MSE[mean]",
@@ -27,8 +28,9 @@ METRIC_COLUMNS = [
27
  ]
28
 
29
  DISPLAY_METRICS = [
30
- "eval_metrics/MASE[0.5]",
31
  "eval_metrics/mean_weighted_sum_quantile_loss",
 
32
  "eval_metrics/MAE[0.5]",
33
  "eval_metrics/RMSE[mean]",
34
  "eval_metrics/sMAPE[0.5]",
@@ -39,6 +41,7 @@ DISPLAY_METRICS = [
39
  ]
40
 
41
  METRIC_LABELS = {
 
42
  "eval_metrics/MASE[0.5]": "MASE",
43
  "eval_metrics/mean_weighted_sum_quantile_loss": "CRPS",
44
  "eval_metrics/MAE[0.5]": "MAE",
@@ -50,8 +53,21 @@ METRIC_LABELS = {
50
  "eval_metrics/MAPE[0.5]": "MAPE",
51
  }
52
 
53
- VALUE_COLUMNS = [METRIC_LABELS[m] for m in DISPLAY_METRICS]
54
- RANK_COLUMNS = [f"{METRIC_LABELS[m]}_Rank" for m in DISPLAY_METRICS]
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  LEGACY_MODEL_NAMES = {
57
  "TSFM1": "Chronos-Bolt-Tiny",
@@ -93,7 +109,10 @@ def format_df(df: pd.DataFrame) -> pd.DataFrame:
93
  formatted = df.copy()
94
  for col in formatted.columns:
95
  if pd.api.types.is_numeric_dtype(formatted[col]):
96
- formatted[col] = formatted[col].map(format_number)
 
 
 
97
  formatted[col] = formatted[col].astype(float)
98
  return formatted
99
 
@@ -207,6 +226,17 @@ def get_grouped_dfs(
207
  overall_ranks = overall_ranks.rename(
208
  columns={f"Rank_{metric}": f"{METRIC_LABELS[metric]}_Rank" for metric in DISPLAY_METRICS}
209
  )
 
 
 
 
 
 
 
 
 
 
 
210
 
211
  datasets = sorted(df["dataset"].dropna().unique())
212
  dataset_values: dict[str, pd.DataFrame] = {}
@@ -235,24 +265,28 @@ def prepare_values_table(values_df: pd.DataFrame) -> pd.DataFrame:
235
  if values_df is None or values_df.empty:
236
  return pd.DataFrame(columns=["Model"] + VALUE_COLUMNS)
237
  table = values_df.reset_index().rename(columns={"model": "Model"})
238
- table = format_df(table)
239
  cols = ["Model"] + [col for col in VALUE_COLUMNS if col in table.columns]
240
  table = table[cols]
241
- if "MASE" in table.columns and not table["MASE"].isna().all():
242
- return table.sort_values(by=["MASE"])
243
- return table
 
 
 
244
 
245
 
246
  def prepare_ranks_table(ranks_df: pd.DataFrame) -> pd.DataFrame:
247
  if ranks_df is None or ranks_df.empty:
248
  return pd.DataFrame(columns=["Model"] + RANK_COLUMNS)
249
  table = ranks_df.reset_index().rename(columns={"model": "Model"})
250
- table = format_df(table)
251
  cols = ["Model"] + [col for col in RANK_COLUMNS if col in table.columns]
252
  table = table[cols]
253
- if "MASE_Rank" in table.columns and not table["MASE_Rank"].isna().all():
254
- return table.sort_values(by=["MASE_Rank"])
255
- return table
 
 
 
256
 
257
 
258
  DOMAIN_DISPLAY = {
@@ -407,8 +441,9 @@ def dataset_section_title(dataset: str, root_dir: str, *, ranks: bool = False) -
407
  BASELINE_RANK_HISTORY_COLUMNS = [
408
  "date",
409
  "model",
410
- "MASE_Rank",
411
  "CRPS_Rank",
 
412
  "MAE_Rank",
413
  "RMSE_Rank",
414
  "sMAPE_Rank",
@@ -599,7 +634,7 @@ def make_rank_trend_plot(history_df: pd.DataFrame, metrics: list[str] | None = N
599
 
600
  Args:
601
  history_df: DataFrame from load_baseline_rank_history() with a 'date' column.
602
- metrics: List of rank column names to plot. Defaults to MASE_Rank and CRPS_Rank.
603
 
604
  Returns None if fewer than 1 data point or plotly unavailable.
605
  """
@@ -608,7 +643,7 @@ def make_rank_trend_plot(history_df: pd.DataFrame, metrics: list[str] | None = N
608
  return None
609
 
610
  if metrics is None:
611
- metrics = ["MASE_Rank", "CRPS_Rank", "MAE_Rank", "RMSE_Rank"]
612
 
613
  date_col = "date" if "date" in history_df.columns else "week"
614
  present_metrics = [m for m in metrics if m in history_df.columns]
 
11
  from scipy import stats
12
 
13
  from src.display.formatting import format_timestamp_utc8
14
+ from src.rank_based_metric import compute_rank_score
15
 
16
  METRIC_COLUMNS = [
17
  "eval_metrics/MSE[mean]",
 
28
  ]
29
 
30
  DISPLAY_METRICS = [
31
+ "eval_metrics/MSE[mean]",
32
  "eval_metrics/mean_weighted_sum_quantile_loss",
33
+ "eval_metrics/MASE[0.5]",
34
  "eval_metrics/MAE[0.5]",
35
  "eval_metrics/RMSE[mean]",
36
  "eval_metrics/sMAPE[0.5]",
 
41
  ]
42
 
43
  METRIC_LABELS = {
44
+ "eval_metrics/MSE[mean]": "MSE",
45
  "eval_metrics/MASE[0.5]": "MASE",
46
  "eval_metrics/mean_weighted_sum_quantile_loss": "CRPS",
47
  "eval_metrics/MAE[0.5]": "MAE",
 
53
  "eval_metrics/MAPE[0.5]": "MAPE",
54
  }
55
 
56
+ RANK_SCORE_COLUMN = "RankScore"
57
+ RANK_SCORE_RANK_COLUMN = "RankScore_Rank"
58
+ RANK_SCORE_BASE_METRICS = [
59
+ "eval_metrics/MSE[mean]",
60
+ "eval_metrics/mean_weighted_sum_quantile_loss",
61
+ ]
62
+
63
+ VALUE_COLUMNS = ["MSE", "CRPS", RANK_SCORE_COLUMN] + [
64
+ METRIC_LABELS[m] for m in DISPLAY_METRICS if METRIC_LABELS[m] not in {"MSE", "CRPS"}
65
+ ]
66
+ RANK_COLUMNS = ["MSE_Rank", "CRPS_Rank", RANK_SCORE_RANK_COLUMN] + [
67
+ f"{METRIC_LABELS[m]}_Rank"
68
+ for m in DISPLAY_METRICS
69
+ if METRIC_LABELS[m] not in {"MSE", "CRPS"}
70
+ ]
71
 
72
  LEGACY_MODEL_NAMES = {
73
  "TSFM1": "Chronos-Bolt-Tiny",
 
109
  formatted = df.copy()
110
  for col in formatted.columns:
111
  if pd.api.types.is_numeric_dtype(formatted[col]):
112
+ if col == RANK_SCORE_COLUMN:
113
+ formatted[col] = formatted[col].map(lambda x: np.nan if pd.isna(x) else f"{x:.1f}")
114
+ else:
115
+ formatted[col] = formatted[col].map(format_number)
116
  formatted[col] = formatted[col].astype(float)
117
  return formatted
118
 
 
226
  overall_ranks = overall_ranks.rename(
227
  columns={f"Rank_{metric}": f"{METRIC_LABELS[metric]}_Rank" for metric in DISPLAY_METRICS}
228
  )
229
+ rank_score = compute_rank_score(
230
+ df,
231
+ metric_columns=RANK_SCORE_BASE_METRICS,
232
+ group_columns=("dataset", "term_length", "frequency"),
233
+ )
234
+ overall_values = overall_values.join(rank_score, how="left")
235
+ if RANK_SCORE_COLUMN in overall_values.columns:
236
+ overall_ranks[RANK_SCORE_RANK_COLUMN] = overall_values[RANK_SCORE_COLUMN].rank(
237
+ method="average",
238
+ ascending=False,
239
+ )
240
 
241
  datasets = sorted(df["dataset"].dropna().unique())
242
  dataset_values: dict[str, pd.DataFrame] = {}
 
265
  if values_df is None or values_df.empty:
266
  return pd.DataFrame(columns=["Model"] + VALUE_COLUMNS)
267
  table = values_df.reset_index().rename(columns={"model": "Model"})
 
268
  cols = ["Model"] + [col for col in VALUE_COLUMNS if col in table.columns]
269
  table = table[cols]
270
+ if "MSE" in table.columns and not table["MSE"].isna().all():
271
+ table = table.sort_values(by=["MSE"])
272
+ return format_df(table)
273
+ if "RankScore" in table.columns and not table["RankScore"].isna().all():
274
+ table = table.sort_values(by=["RankScore"], ascending=False)
275
+ return format_df(table)
276
 
277
 
278
  def prepare_ranks_table(ranks_df: pd.DataFrame) -> pd.DataFrame:
279
  if ranks_df is None or ranks_df.empty:
280
  return pd.DataFrame(columns=["Model"] + RANK_COLUMNS)
281
  table = ranks_df.reset_index().rename(columns={"model": "Model"})
 
282
  cols = ["Model"] + [col for col in RANK_COLUMNS if col in table.columns]
283
  table = table[cols]
284
+ if "MSE_Rank" in table.columns and not table["MSE_Rank"].isna().all():
285
+ table = table.sort_values(by=["MSE_Rank"])
286
+ return format_df(table)
287
+ if RANK_SCORE_RANK_COLUMN in table.columns and not table[RANK_SCORE_RANK_COLUMN].isna().all():
288
+ table = table.sort_values(by=[RANK_SCORE_RANK_COLUMN])
289
+ return format_df(table)
290
 
291
 
292
  DOMAIN_DISPLAY = {
 
441
  BASELINE_RANK_HISTORY_COLUMNS = [
442
  "date",
443
  "model",
444
+ "MSE_Rank",
445
  "CRPS_Rank",
446
+ "MASE_Rank",
447
  "MAE_Rank",
448
  "RMSE_Rank",
449
  "sMAPE_Rank",
 
634
 
635
  Args:
636
  history_df: DataFrame from load_baseline_rank_history() with a 'date' column.
637
+ metrics: List of rank column names to plot. Defaults to MSE_Rank and CRPS_Rank.
638
 
639
  Returns None if fewer than 1 data point or plotly unavailable.
640
  """
 
643
  return None
644
 
645
  if metrics is None:
646
+ metrics = ["MSE_Rank", "CRPS_Rank", "MASE_Rank", "MAE_Rank"]
647
 
648
  date_col = "date" if "date" in history_df.columns else "week"
649
  present_metrics = [m for m in metrics if m in history_df.columns]