tsfp-repro-code / audit_table1_rank.py
riteshhf's picture
Upload folder using huggingface_hub
2188a91 verified
Raw
History Blame Contribute Delete
4.73 kB
"""Claim 5 audit: recompute the reported average rank of 1.24 from Table 1 itself.
Table 1 is parsed straight out of the arXiv HTML text dump (paper/paper.txt) so the
numbers are not re-typed by hand. We then rank the 12 methods on every
(dataset, metric) cell and report the mean rank of "Ours" under several readings of
the claim, since the paper is ambiguous about which metrics enter the average
(Sec 4.1 says "across all metrics", the challenge statement says accuracy/F1/AUROC).
"""
import json
import re
import numpy as np
METHODS = ["Autoformer", "Crossformer", "FEDformer", "Informer", "iTransformer",
"MTST", "Nonformer", "PatchTST", "Reformer", "Transformer",
"Medformer", "Ours"]
DATASETS = ["ADFTD", "PTB", "PTB-XL", "APAVA", "SleepEDF", "FLAAP", "UCI-HAR"]
METRICS = ["Accuracy", "Precision", "Recall", "F1-score", "AUROC"]
def parse(path="paper/paper.txt"):
txt = open(path, encoding="utf-8").read()
# Table 1 spans from the header block to the "Table 1 :" caption
start = txt.index(" Dataset \n Metrics \n Autoformer")
end = txt.index(" Table 1 : ")
body = txt[start:end]
# every "value ± std" pair, in reading order
vals = [float(m) for m in re.findall(r"(\d+\.\d+)\s*±", body)]
assert len(vals) == 7 * 5 * 12, f"expected 420 numbers, parsed {len(vals)}"
arr = np.array(vals).reshape(7, 5, 12)
return arr
def ranks_for_ours(arr, metric_idx):
"""Rank (1 = best, higher value is better for every metric here) of 'Ours'."""
out = {}
for di, ds in enumerate(DATASETS):
for mi in metric_idx:
col = arr[di, mi]
# competition ranking with ties sharing the average rank
order = (-col).argsort()
r = np.empty(12)
r[order] = np.arange(1, 13)
# handle exact ties
for v in np.unique(col):
m = col == v
if m.sum() > 1:
r[m] = r[m].mean()
out[(ds, METRICS[mi])] = float(r[METHODS.index("Ours")])
return out
def main():
arr = parse()
report = {"table_shape": list(arr.shape)}
# spot-check a few known cells against the paper text
checks = {
("ADFTD", "Accuracy", "Ours"): 53.91,
("ADFTD", "F1-score", "Ours"): 51.79,
("ADFTD", "F1-score", "Medformer"): 50.65,
("PTB-XL", "F1-score", "Ours"): 63.51,
("UCI-HAR", "Accuracy", "Medformer"): 91.65,
("SleepEDF", "AUROC", "Ours"): 95.37,
}
ok = {}
for (ds, me, mo), want in checks.items():
got = arr[DATASETS.index(ds), METRICS.index(me), METHODS.index(mo)]
ok[f"{ds}/{me}/{mo}"] = {"parsed": float(got), "paper": want,
"match": bool(abs(got - want) < 1e-9)}
report["parse_spot_checks"] = ok
variants = {
"all_5_metrics": [0, 1, 2, 3, 4],
"acc_f1_auroc (challenge wording)": [0, 3, 4],
"acc_f1": [0, 3],
}
for name, idx in variants.items():
r = ranks_for_ours(arr, idx)
vals = np.array(list(r.values()))
report[name] = {
"n_cells": int(vals.size),
"avg_rank_ours": float(vals.mean()),
"n_rank1": int((vals == 1).sum()),
"worst_rank": float(vals.max()),
"cells_not_rank1": {f"{k[0]}/{k[1]}": v for k, v in r.items() if v != 1},
}
# average rank of every method under the 5-metric reading, for context
allranks = {}
for mo in METHODS:
rs = []
for di in range(7):
for mi in range(5):
col = arr[di, mi]
order = (-col).argsort()
rr = np.empty(12)
rr[order] = np.arange(1, 13)
for v in np.unique(col):
m = col == v
if m.sum() > 1:
rr[m] = rr[m].mean()
rs.append(rr[METHODS.index(mo)])
allranks[mo] = float(np.mean(rs))
report["avg_rank_all_methods_5metrics"] = allranks
# one-tailed Wilcoxon signed-rank, Ours vs Medformer (paper's stated test)
from scipy.stats import wilcoxon
ours = arr[:, :, METHODS.index("Ours")].ravel()
med = arr[:, :, METHODS.index("Medformer")].ravel()
st, p = wilcoxon(ours, med, alternative="greater")
report["wilcoxon_ours_gt_medformer"] = {"statistic": float(st), "p_value": float(p),
"n_pairs": int(ours.size),
"ours_wins": int((ours > med).sum())}
print(json.dumps(report, indent=2))
with open("results/table1_rank_audit.json", "w") as f:
json.dump(report, f, indent=2)
if __name__ == "__main__":
main()