SPB Git forge

spb/wp23_uqo

Public
4commits 1branches 0releases
28.2 MBsize
maindefault branch
13 min agolast push
TeX 55.5% Python 44.5%

WP23: robustness section and table

Simon-Pierre Boucher committed 1 h ago (Oct 21, 2026) parent fb8fc70

6 changed files +224 −2

modified paper/main.pdf +0 −0

Binary file not shown.

modified paper/sections/ablation.tex +3 −1
@@ -10,4 +10,6 @@ The feature-group ablations of the pooled ridge model locate the predictable sig
10 10
11 11 \subsection{Robustness}
12 12
13 −%%ROBUST%%
13 +Table~\ref{tab:robust_b} re-runs a subset of Task~B models at the three-month horizon under alternative designs. Four facts are stable. Rolling 24-origin windows help the pooled ridge (\RobridgepanelrollingTwoFour{} against \Robridgepanelexpanding{} log points, as parameter drift in a short sample favours recent data) and hurt the neural models, which need the observations. Dropping the thinnest cell-months ($n_{\text{last}} < 10$) or winsorising the targets changes every model by less than 0.2 log points and leaves the ranking intact. The DSGFN bandwidth (30 or 120 km instead of 60) is irrelevant, consistent with the ablation that the graph carries no information. Most importantly, re-estimating the neural models with different seeds moves their error by as much as the differences between models: DSGFN ranges from \RobdsgfnseedTwo{} to \RobdsgfnseedOne{} log points across three seeds and TFT-lite from \Robtftliteexpanding{} to \RobtftliteseedTwo, while the ridge and AR benchmarks have no seed. With 45 origins and 64 cells the neural models are not reliably distinguishable from one another, and any ranking among them in Section~\ref{sec:results} should be read with this seed variability in mind; the proposed QANSS is the most stable of the three (\Robqanssexpanding{}, \RobqanssseedOne{}, \RobqanssseedTwo).
14 +
15 +\input{tables/tab_robust_b}
modified paper/tables/numbers.tex +72 −0
@@ -115,6 +115,78 @@
115 115 \newcommand{\NRowsB}{39{,}690}
116 116 \newcommand{\NSales}{632{,}006}
117 117 \newcommand{\NTestA}{1{,}255{,}898}
118 +\newcommand{\RobCovarTwocellexpanding}{91}
119 +\newcommand{\RobCovarTwocellminnOneZero}{91}
120 +\newcommand{\RobCovarTwocellrollingTwoFour}{91}
121 +\newcommand{\RobCovarTwocellwinsorised}{91}
122 +\newcommand{\RobCovdsgfnbwOneTwoZerobandwidth}{74}
123 +\newcommand{\RobCovdsgfnbwThreeZerobandwidth}{76}
124 +\newcommand{\RobCovdsgfnexpanding}{76}
125 +\newcommand{\RobCovdsgfnminnOneZero}{73}
126 +\newcommand{\RobCovdsgfnrollingTwoFour}{71}
127 +\newcommand{\RobCovdsgfnseedOne}{73}
128 +\newcommand{\RobCovdsgfnseedTwo}{78}
129 +\newcommand{\RobCovdsgfnwinsorised}{77}
130 +\newcommand{\RobCovmomentumexpanding}{91}
131 +\newcommand{\RobCovmomentumminnOneZero}{91}
132 +\newcommand{\RobCovmomentumrollingTwoFour}{91}
133 +\newcommand{\RobCovmomentumwinsorised}{91}
134 +\newcommand{\RobCovnochangeexpanding}{91}
135 +\newcommand{\RobCovnochangeminnOneZero}{91}
136 +\newcommand{\RobCovnochangerollingTwoFour}{90}
137 +\newcommand{\RobCovnochangewinsorised}{91}
138 +\newcommand{\RobCovqanssexpanding}{97}
139 +\newcommand{\RobCovqanssminnOneZero}{92}
140 +\newcommand{\RobCovqanssrollingTwoFour}{96}
141 +\newcommand{\RobCovqanssseedOne}{96}
142 +\newcommand{\RobCovqanssseedTwo}{96}
143 +\newcommand{\RobCovqansswinsorised}{96}
144 +\newcommand{\RobCovridgepanelexpanding}{88}
145 +\newcommand{\RobCovridgepanelminnOneZero}{87}
146 +\newcommand{\RobCovridgepanelrollingTwoFour}{88}
147 +\newcommand{\RobCovridgepanelwinsorised}{88}
148 +\newcommand{\RobCovtftliteexpanding}{73}
149 +\newcommand{\RobCovtftliteminnOneZero}{71}
150 +\newcommand{\RobCovtftliterollingTwoFour}{68}
151 +\newcommand{\RobCovtftliteseedOne}{70}
152 +\newcommand{\RobCovtftliteseedTwo}{67}
153 +\newcommand{\RobCovtftlitewinsorised}{72}
154 +\newcommand{\RobarTwocellexpanding}{3.95}
155 +\newcommand{\RobarTwocellminnOneZero}{4.06}
156 +\newcommand{\RobarTwocellrollingTwoFour}{3.92}
157 +\newcommand{\RobarTwocellwinsorised}{3.82}
158 +\newcommand{\RobdsgfnbwOneTwoZerobandwidth}{3.98}
159 +\newcommand{\RobdsgfnbwThreeZerobandwidth}{4.00}
160 +\newcommand{\Robdsgfnexpanding}{3.76}
161 +\newcommand{\RobdsgfnminnOneZero}{3.83}
162 +\newcommand{\RobdsgfnrollingTwoFour}{3.90}
163 +\newcommand{\RobdsgfnseedOne}{4.05}
164 +\newcommand{\RobdsgfnseedTwo}{3.66}
165 +\newcommand{\Robdsgfnwinsorised}{3.76}
166 +\newcommand{\Robmomentumexpanding}{4.39}
167 +\newcommand{\RobmomentumminnOneZero}{4.40}
168 +\newcommand{\RobmomentumrollingTwoFour}{4.38}
169 +\newcommand{\Robmomentumwinsorised}{4.25}
170 +\newcommand{\Robnochangeexpanding}{4.38}
171 +\newcommand{\RobnochangeminnOneZero}{4.40}
172 +\newcommand{\RobnochangerollingTwoFour}{4.38}
173 +\newcommand{\Robnochangewinsorised}{4.24}
174 +\newcommand{\Robqanssexpanding}{3.75}
175 +\newcommand{\RobqanssminnOneZero}{3.69}
176 +\newcommand{\RobqanssrollingTwoFour}{3.88}
177 +\newcommand{\RobqanssseedOne}{3.96}
178 +\newcommand{\RobqanssseedTwo}{3.87}
179 +\newcommand{\Robqansswinsorised}{3.72}
180 +\newcommand{\Robridgepanelexpanding}{3.73}
181 +\newcommand{\RobridgepanelminnOneZero}{3.63}
182 +\newcommand{\RobridgepanelrollingTwoFour}{3.52}
183 +\newcommand{\Robridgepanelwinsorised}{3.59}
184 +\newcommand{\Robtftliteexpanding}{4.33}
185 +\newcommand{\RobtftliteminnOneZero}{4.12}
186 +\newcommand{\RobtftliterollingTwoFour}{4.46}
187 +\newcommand{\RobtftliteseedOne}{4.51}
188 +\newcommand{\RobtftliteseedTwo}{4.83}
189 +\newcommand{\Robtftlitewinsorised}{4.21}
118 190 \newcommand{\SelAgecondoSold}{17}
119 191 \newcommand{\SelAgecondoStock}{20}
120 192 \newcommand{\SelAgehouseSold}{41}
added paper/tables/tab_robust_b.tex +22 −0
@@ -0,0 +1,22 @@
1 +\begin{table}[htbp]
2 +\centering\scriptsize
3 +\caption{Task B robustness at the municipality level, horizon 3: MAE (×100) under alternative designs}\label{tab:robust_b}
4 +\begin{threeparttable}
5 +\begin{tabular}{lrrrrrrr}
6 +\toprule
7 +Model & Expanding (baseline) & Rolling 24 origins & Cells with $n \ge 10$ & Winsorised target & Seed 1 & Seed 2 & Bandwidth 30 / 120 km \\
8 +\midrule
9 +No change & 4.38 & 4.38 & 4.40 & 4.24 & — & — & — \\
10 +Momentum & 4.39 & 4.38 & 4.40 & 4.25 & — & — & — \\
11 +AR(2), cell & 3.95 & 3.92 & 4.06 & 3.82 & — & — & — \\
12 +Ridge (panel) & 3.73 & 3.52 & 3.63 & 3.59 & — & — & — \\
13 +QANSS & 3.75 & 3.88 & 3.69 & 3.72 & 3.96 & 3.87 & — \\
14 +TFT-lite & 4.33 & 4.46 & 4.12 & 4.21 & 4.51 & 4.83 & — \\
15 +DSGFN & 3.76 & 3.90 & 3.83 & 3.76 & 4.05 & 3.66 & — \\
16 +DSGFN, bandwidth 30 km & — & — & — & — & — & — & 4.00 \\
17 +DSGFN, bandwidth 120 km & — & — & — & — & — & — & 3.98 \\
18 +\bottomrule
19 +\end{tabular}
20 +\begin{tablenotes}\footnotesize\item Rolling: models estimated on the 24 most recent origins only. Seeds: neural models re-estimated with different random seeds (the baseline is seed 0). Winsorised: targets clipped at the 1st and 99th percentiles in training and test.\end{tablenotes}
21 +\end{threeparttable}
22 +\end{table}
added results/robustness_b/summary.csv +109 −0
@@ -0,0 +1,109 @@
1 +variant,model,level,n,mae,cov90
2 +bandwidth,dsgfn_bw120,municipality,5952,0.03976347029955593,0.7436155913978495
3 +bandwidth,dsgfn_bw30,municipality,5952,0.03995266383849688,0.7592405913978495
4 +expanding,ridge_panel,municipality,5952,0.03732290190701437,0.8781922043010753
5 +expanding,qanss,municipality,5952,0.037459654537622296,0.9689180107526881
6 +expanding,dsgfn,municipality,5952,0.03757560885168484,0.7636088709677419
7 +expanding,ar2_cell,municipality,5952,0.03947945934370948,0.9096102150537635
8 +expanding,tft_lite,municipality,5952,0.04332872596968457,0.7289986559139785
9 +expanding,no_change,municipality,5952,0.04376674236023525,0.9064180107526881
10 +expanding,momentum,municipality,5952,0.04393421044560428,0.9122983870967742
11 +min_n_10,ridge_panel,municipality,3221,0.036266293642619435,0.8677429369760944
12 +min_n_10,qanss,municipality,3221,0.03686983996180768,0.9195901893821794
13 +min_n_10,dsgfn,municipality,3221,0.038298265191224805,0.7336230984166408
14 +min_n_10,ar2_cell,municipality,3221,0.040591029944037155,0.9140018627755355
15 +min_n_10,tft_lite,municipality,3221,0.04115923356205413,0.7066128531511953
16 +min_n_10,momentum,municipality,3221,0.043960382827593084,0.9130704750077616
17 +min_n_10,no_change,municipality,3221,0.043977006065149246,0.9102763117044396
18 +rolling_24,ridge_panel,municipality,5952,0.03518223489612539,0.8822244623655914
19 +rolling_24,qanss,municipality,5952,0.038832460583841766,0.9643817204301075
20 +rolling_24,dsgfn,municipality,5952,0.03895731526580927,0.7132056451612904
21 +rolling_24,ar2_cell,municipality,5952,0.039246363132674954,0.905241935483871
22 +rolling_24,no_change,municipality,5952,0.04376674236023525,0.9000336021505376
23 +rolling_24,momentum,municipality,5952,0.04383562647985487,0.9077620967741935
24 +rolling_24,tft_lite,municipality,5952,0.04455747749904916,0.6843077956989247
25 +seed_1,qanss,municipality,5952,0.03963692364231179,0.9620295698924731
26 +seed_1,dsgfn,municipality,5952,0.04053324927199185,0.7288306451612904
27 +seed_1,tft_lite,municipality,5952,0.04512192996764146,0.7012768817204301
28 +seed_2,dsgfn,municipality,5952,0.03662661300967685,0.7778897849462365
29 +seed_2,qanss,municipality,5952,0.038662565873541964,0.961861559139785
30 +seed_2,tft_lite,municipality,5952,0.048323387946891935,0.6685147849462365
31 +winsorised,ridge_panel,municipality,5952,0.035902447871244185,0.8780241935483871
32 +winsorised,qanss,municipality,5952,0.03721128622448282,0.9642137096774194
33 +winsorised,dsgfn,municipality,5952,0.03757660752807548,0.7740255376344086
34 +winsorised,ar2_cell,municipality,5952,0.03816003837310943,0.9084341397849462
35 +winsorised,tft_lite,municipality,5952,0.042114816903162704,0.7231182795698925
36 +winsorised,no_change,municipality,5952,0.04241714135136217,0.9064180107526881
37 +winsorised,momentum,municipality,5952,0.04246080839666102,0.9119623655913979
38 +bandwidth,dsgfn_bw30,province,93,0.0192133209806252,0.6881720430107527
39 +bandwidth,dsgfn_bw120,province,93,0.019485176204455126,0.7096774193548387
40 +expanding,dsgfn,province,93,0.017160920569865058,0.6666666666666666
41 +expanding,qanss,province,93,0.017711344707247292,0.978494623655914
42 +expanding,ar2_cell,province,93,0.020592420659073678,1.0
43 +expanding,ridge_panel,province,93,0.02063614099129669,1.0
44 +expanding,tft_lite,province,93,0.020995608506259728,0.6774193548387096
45 +expanding,no_change,province,93,0.02436499449491245,1.0
46 +expanding,momentum,province,93,0.027343649445911486,1.0
47 +min_n_10,qanss,province,93,0.019383603423469193,0.956989247311828
48 +min_n_10,dsgfn,province,93,0.019519816352457208,0.5806451612903226
49 +min_n_10,ar2_cell,province,93,0.02087071431642656,1.0
50 +min_n_10,tft_lite,province,93,0.02257131637117071,0.5483870967741935
51 +min_n_10,no_change,province,93,0.02436499449491245,1.0
52 +min_n_10,ridge_panel,province,93,0.024813917125404015,1.0
53 +min_n_10,momentum,province,93,0.027022911205361478,1.0
54 +rolling_24,ridge_panel,province,93,0.015946147559918196,1.0
55 +rolling_24,dsgfn,province,93,0.017215854155773886,0.6236559139784946
56 +rolling_24,ar2_cell,province,93,0.02047394230387744,1.0
57 +rolling_24,qanss,province,93,0.020655558114102322,0.967741935483871
58 +rolling_24,tft_lite,province,93,0.022728178088914103,0.6451612903225806
59 +rolling_24,no_change,province,93,0.02436499449491245,1.0
60 +rolling_24,momentum,province,93,0.026935211993645215,1.0
61 +seed_1,dsgfn,province,93,0.018455692684076944,0.6344086021505376
62 +seed_1,qanss,province,93,0.01916255794163663,0.978494623655914
63 +seed_1,tft_lite,province,93,0.03122934245043022,0.6344086021505376
64 +seed_2,dsgfn,province,93,0.014938262936382169,0.8172043010752689
65 +seed_2,qanss,province,93,0.019531679910748296,0.989247311827957
66 +seed_2,tft_lite,province,93,0.03042892584054891,0.5591397849462365
67 +winsorised,dsgfn,province,93,0.017441387108336853,0.6666666666666666
68 +winsorised,qanss,province,93,0.01913613081548442,0.978494623655914
69 +winsorised,tft_lite,province,93,0.02024786060756957,0.7096774193548387
70 +winsorised,ar2_cell,province,93,0.02051826790189626,1.0
71 +winsorised,ridge_panel,province,93,0.02062736802657889,1.0
72 +winsorised,no_change,province,93,0.02436499449491245,1.0
73 +winsorised,momentum,province,93,0.026917608487505476,1.0
74 +bandwidth,dsgfn_bw120,region,1550,0.027726215357003026,0.7625806451612903
75 +bandwidth,dsgfn_bw30,region,1550,0.029408703976601884,0.7774193548387097
76 +expanding,qanss,region,1550,0.025719806408649216,0.9619354838709677
77 +expanding,dsgfn,region,1550,0.027645981026585394,0.7445161290322581
78 +expanding,ridge_panel,region,1550,0.027783691370929776,0.9574193548387097
79 +expanding,ar2_cell,region,1550,0.03137389803013293,0.9587096774193549
80 +expanding,tft_lite,region,1550,0.03454355140349113,0.7503225806451613
81 +expanding,no_change,region,1550,0.03576524903693651,0.9632258064516129
82 +expanding,momentum,region,1550,0.03731020650246961,0.9606451612903226
83 +min_n_10,qanss,region,1307,0.028285538801507328,0.8982402448355011
84 +min_n_10,ridge_panel,region,1307,0.029272685961525595,0.9357306809487376
85 +min_n_10,dsgfn,region,1307,0.029581992399314756,0.7505738332058148
86 +min_n_10,tft_lite,region,1307,0.033241582646857844,0.7276205049732212
87 +min_n_10,ar2_cell,region,1307,0.03409099933177777,0.9556235654169855
88 +min_n_10,no_change,region,1307,0.03779962669260435,0.9556235654169855
89 +min_n_10,momentum,region,1307,0.039097356704781594,0.9563886763580719
90 +rolling_24,ridge_panel,region,1550,0.025687763978500634,0.9612903225806452
91 +rolling_24,qanss,region,1550,0.02715135781327226,0.9554838709677419
92 +rolling_24,dsgfn,region,1550,0.027816067027544925,0.7464516129032258
93 +rolling_24,ar2_cell,region,1550,0.03122754772176746,0.9548387096774194
94 +rolling_24,no_change,region,1550,0.03576524903693651,0.9548387096774194
95 +rolling_24,tft_lite,region,1550,0.03644000914715816,0.6870967741935484
96 +rolling_24,momentum,region,1550,0.037038368350340904,0.9548387096774194
97 +seed_1,qanss,region,1550,0.028120027756348365,0.952258064516129
98 +seed_1,dsgfn,region,1550,0.03068361945794511,0.6941935483870968
99 +seed_1,tft_lite,region,1550,0.03763784778383622,0.7070967741935484
100 +seed_2,dsgfn,region,1550,0.025062814448091615,0.8141935483870968
101 +seed_2,qanss,region,1550,0.02777277000969687,0.9496774193548387
102 +seed_2,tft_lite,region,1550,0.0432579391528578,0.6277419354838709
103 +winsorised,qanss,region,1550,0.02695186071177874,0.9503225806451613
104 +winsorised,dsgfn,region,1550,0.027367628711845754,0.7825806451612903
105 +winsorised,ridge_panel,region,1550,0.02759247572841403,0.952258064516129
106 +winsorised,ar2_cell,region,1550,0.03129478996279187,0.9561290322580646
107 +winsorised,tft_lite,region,1550,0.03457136483137272,0.7264516129032258
108 +winsorised,no_change,region,1550,0.03571433058060569,0.9632258064516129
109 +winsorised,momentum,region,1550,0.03698824259555123,0.9612903225806452
modified scripts/50_tables.py +18 −1
@@ -191,6 +191,23 @@ def t_ablation():
191 191 macro(f"AttTop{r['h']}", r["top_weight"], "{:.2f}"); macro(f"AttDist{r['h']}", r["median_dist_top_km"], "{:.0f}"); macro(f"AttSameReg{r['h']}", 100 * r["same_region"], "{:.0f}"); macro(f"AttSameMrc{r['h']}", 100 * r["same_mrc"], "{:.0f}")
192 192
193 193
194 +def t_robust():
195 + f = R / "robustness_b" / "summary.csv"
196 + if not ok(f): return
197 + d = pl.read_csv(f, infer_schema_length=None).filter(pl.col("level") == "municipality")
198 + variants = ["expanding", "rolling_24", "min_n_10", "winsorised", "seed_1", "seed_2", "bandwidth"]; labv = {"expanding": "Expanding (baseline)", "rolling_24": "Rolling 24 origins", "min_n_10": "Cells with $n \\ge 10$", "winsorised": "Winsorised target", "seed_1": "Seed 1", "seed_2": "Seed 2", "bandwidth": "Bandwidth 30 / 120 km"}
199 + models = ["no_change", "momentum", "ar2_cell", "ridge_panel", "qanss", "tft_lite", "dsgfn", "dsgfn_bw30", "dsgfn_bw120"]
200 + rows = []
201 + for m in models:
202 + r = [LABELS.get(m, m.replace("dsgfn_bw", "DSGFN, bandwidth ").replace("30", "30 km").replace("120", "120 km") if m.startswith("dsgfn_bw") else LABELS.get(m, m))]
203 + for v in variants:
204 + x = d.filter((pl.col("model") == m) & (pl.col("variant") == v)); r.append(f"{100*x['mae'][0]:.2f}" if x.height else "—")
205 + if x.height: macro(f"Rob{m}{v}", 100 * x["mae"][0], "{:.2f}"); macro(f"RobCov{m}{v}", 100 * x["cov90"][0], "{:.0f}")
206 + rows.append(r)
207 + table("tab_robust_b", ["Model"] + [labv[v] for v in variants], rows, "Task B robustness at the municipality level, horizon 3: MAE (×100) under alternative designs", "tab:robust_b", size="\\scriptsize",
208 + notes="Rolling: models estimated on the 24 most recent origins only. Seeds: neural models re-estimated with different random seeds (the baseline is seed 0). Winsorised: targets clipped at the 1st and 99th percentiles in training and test.")
209 +
210 +
194 211 def t_interpret():
195 212 f = R / "interpret" / "shap_importance_wide.csv"
196 213 if not ok(f): return
@@ -228,7 +245,7 @@ def t_simulation():
228 245
229 246
230 247 if __name__ == "__main__":
231 − for fn in [t_data, t_results, t_ablation, t_interpret, t_simulation]:
248 + for fn in [t_data, t_results, t_ablation, t_robust, t_interpret, t_simulation]:
232 249 try: fn()
233 250 except Exception as ex:
234 251 import traceback; traceback.print_exc(); print("FAILED", fn.__name__)
235 252