PoPTard5 / model-3 /scripts /fr_robust.py
Jyo-K's picture
Upload folder using huggingface_hub
e79d917 verified
Raw History Blame Contribute Delete
3.63 kB
"""Risk analysis for raising France's threshold.
The p**g shift model saturated at g=6 without fully explaining France's deficit, so g is
NOT identified. The decision must therefore be robust across the whole plausible range of
g - including g=1 (no shift at all), which is the downside case. Reports, for each g:
the optimal France threshold, and what threshold 0.90 costs or gains against 0.70.
Also propagates the uncertainty in the val US/India estimate into France's implied score,
since France is inferred by subtraction and the lever arm is 0.85/0.15 = 5.7x.
"""
import sys, glob
import numpy as np, pandas as pd, joblib
sys.path.insert(0, "src")
import config as C, decide as DC
b = joblib.load("work/results_A/model_bundle.joblib")
MM, THR = b["miss_mass"], b["param"]
s1 = pd.read_parquet(C.PATHS["test"]["source1"], columns=["entity_id", "country_norm"])
ctry = s1["country_norm"].astype(str).values
N = len(s1)
nq = {c: int((ctry == c).sum()) for c in np.unique(ctry)}
wfr = nq["france"] / N
d = pd.concat([pd.read_parquet(f) for f in
sorted(glob.glob("work/results_A/test_chunks/chunk_*.parquet"))],
ignore_index=True)
d = DC.resolve_conflicts(d, "p")
d["ctry"] = ctry[d["q"].values]
fr = d[d.ctry == "france"][["q", "p"]].sort_values("q", kind="stable")
fq, fp = fr["q"].values, fr["p"].values.astype(np.float64)
uq, starts = np.unique(fq, return_index=True)
idx = np.r_[starts, len(fq)]
n_fr, n_nocand = nq["france"], nq["france"] - len(uq)
def f05(g, t):
pg = fp ** g
sel = fp >= t
tot = float(n_nocand)
for i in range(len(uq)):
a, z = idx[i], idx[i + 1]
m = sel[a:z]
k = int(m.sum())
tp = float(pg[a:z][m].sum())
T = tp + float(pg[a:z][~m].sum()) + MM
tot += (np.exp(-T) if T > 0 else 1.0) if k == 0 else 1.25 * tp / (0.25 * T + k)
return tot / n_fr
print("=" * 78)
print("ROBUSTNESS: France threshold choice across the whole plausible shift range")
print("=" * 78)
grid = [0.70, 0.75, 0.80, 0.85, 0.88, 0.90, 0.92, 0.95]
print(f"{'g':>5}{'F@0.70':>10}" + "".join(f"{'d@'+format(t,'.2f'):>10}" for t in grid[1:])
+ f"{' best thr':>11}")
for g in [1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0]:
base = f05(g, 0.70)
vals = [(t, f05(g, t)) for t in grid]
bt, bv = max(vals, key=lambda x: x[1])
print(f"{g:>5.1f}{base:>10.5f}" + "".join(f"{v-base:>+10.5f}" for _, v in vals[1:])
+ f"{bt:>11.2f}")
print("\n" + "=" * 78)
print("DOWNSIDE AT g=1 (calibration transfers perfectly - the pessimistic case for")
print("this change) vs UPSIDE at g>=3, expressed on the LEADERBOARD")
print("=" * 78)
for g in [1.0, 2.0, 3.0, 4.0, 6.0]:
base = f05(g, 0.70)
for t in (0.85, 0.90):
print(f" g={g:<4} thr {t:.2f}: France {f05(g,t)-base:+.6f} "
f"-> leaderboard {wfr*(f05(g,t)-base):+.6f}")
print("\n" + "=" * 78)
print("HOW MUCH OF FRANCE'S DEFICIT CAN MISCALIBRATION EXPLAIN AT ALL?")
print("=" * 78)
print(f" at threshold 0.70: g=1 -> {f05(1.0,0.70):.6f} g=6 -> {f05(6.0,0.70):.6f}")
print(" implied from leaderboard: 0.946062")
print(" => even an extreme shift leaves a residual, so part of France's deficit is")
print(" NOT the operating point: it is true pairs never retrieved or never scored.")
print("\n" + "=" * 78)
print("SENSITIVITY of France's implied score to the val US/India estimate")
print("=" * 78)
wus, win = nq["us"] / N, nq["india"] / N
for dv in (-0.003, -0.002, -0.001, 0.0, 0.001, 0.002, 0.003):
usin = wus * (0.9832 + dv) + win * (0.9806 + dv)
print(f" val US/India {0.98177+dv:.5f} -> France {(0.976423-usin)/wfr:.5f}")