SPB Git

spb/wp17_uqo

Public

FIRC0002 Stage F NB-01..11 (verifier's non-blocking items): VW-weight universes made explicit, ordinals generated, bib/hook housekeeping, Table 3 max named

NB-1  scripts/r3k_20_tables_v2.py: the VW concentration diagnostic is emitted for two universes read from
      results/r3k_v2/vw_weights_diag.csv and asserted equal to results.json vw_diag -- all nine full-sample VW
      sorts (max 36 %, cybersecurity placebo sort, AAPL, 2023 cohort, quintile 2; 18 entries = 9 distinct cells
      above 25 %) and the composite sort alone (max 19 %, AAPL, 2011 cohort, quintile 4; no cell above 25 %).
      New macros \mvwMaxWeightAll/AllSort/AllCell, \mvwCellsAboveAll/AllDistinct, \mvwSortsAboveAll,
      \mvwMaxWeightCompos/ComposCell, \mvwCellsAboveCompos (+ v2.0 composite values). Stated in the Table 4
      note (paper/tables/tab_sorts_both.tex), the appendix sorts note (tab_sorts.tex), two rows of the hygiene
      Panel B (tab_hygiene.tex), the data-hygiene sentence (paper/sections/data.tex) and README.md.
NB-4  scripts/stageE_writing_macros.py: ordinal() helper, *Ord macros emitted with the ranks/percentiles
      (\mIdaRankOrd ... \mHeRankCoreOrd, \mUtilSectorRankOrd, \mProbe*PctOrd); r3k_20 adds \mplaceboRankOrd/
      \mplaceboRankWOrd. Hard-coded suffixes removed from robustness.tex, data.tex, literature.tex, results.tex.
NB-5  paper/sections/literature.tex: eisfeldt2023generative cited as the first circulated version of the JF 2026
      paper (entry kept, now printed in the bibliography).
NB-6  paper/main.tex: redundant threeparttable -> adjustbox{max width} hook removed; build unchanged
      (92 pages, 0 errors, 0 undefined, 2 overfull boxes of 4.42 pt, none > 5 pt).
NB-11 scripts/r3k_20_tables_v2.py: Table 3 note names the firm-year of the maximum market-adjusted forward
      return (GME, formation year 2019, window 2020-06-30 to 2021-06-30, verdict confirmed on FinData OS bars),
      read from the panel and hygiene_log.csv; macros \mmaxMadjRetTicker/Year/Pct.
NB-2 (reproducibility/FIRC-0002/validation_report.md), NB-9 (jof250 parser note), NB-3/7/8/10 (author or venue
      decisions) are recorded outside the worktree (improvements/FIRC-0002/unresolved_issues.md "Stage F",
      manuscripts/FIRC-0002/change_log_{writing,exhibits}.md).
Build: latexmk clean, rc 0; tests/test_regenerate.py --quick OK; paper/main.pdf rebuilt (92 pages).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Nov 5, 2026) parent 2bd01e8 Browse files

16 changed files +120 −35

modified README.md +2 −1
@@ -21,7 +21,8 @@
21 21 | Object | Estimate (v2.1) |
22 22 |---|---|
23 23 | Panel | 27,354 firm-years, 2,375 firms, formation years 2010–2024 (6,719 firm-years of S&P 500 members); corrected market capitalization, cleaned returns (14 series breaks removed), CIK key |
24 −| Full-sample Q5–Q1 spread (composite) | EW raw +5.5 (NW t 2.40); EW winsorized +3.9 (NW t 2.00, plain 1.78, CI [-0.8, +8.6]); median +0.5; VW (corrected cap) +7.4 (t 2.69); S&P 500 members EW winsorized +5.7 |
24 +| Full-sample Q5–Q1 spread (composite) | EW raw +5.5 (NW t 2.40); EW winsorized +3.9 (NW t 2.00, plain 1.78, CI [-0.8, +8.6]); median +0.5; VW (corrected cap) +7.4 (t 2.69; 10 %-capped +7.2, t 2.62); S&P 500 members EW winsorized +5.7 |
25 +| VW concentration (single-firm weight in a cohort × quintile cell) | composite sort: max 19 % (AAPL, 2011 cohort, quintile 4), no cell above 25 % (the Stage D criterion holds for the composite); across all nine VW sorts: max 36 % in the cybersecurity placebo sort (AAPL, 2023 cohort, quintile 2), 9 distinct cells above 25 %, all in the cybersecurity and SPID sorts (18 entries counting the raw and winsorized variants; committed v2.0 capitalization: 100 %, 548) |
25 26 | Fama–MacBeth slope (composite) | winsorized +1.88 pp/SD (NW t 2.41, plain 2.51, CI [+0.27, +3.49]); raw +2.77 (t 2.44); with sector dummies, winsorized +0.88 (NW 2.80, plain 2.74); sector×year FE +0.75 (firm t 2.04, two-way 2.78) |
26 27 | Pre / post ChatGPT, Russell 3000 (winsorized) | +3.4 → +6.0 (post CI [-25.5, +37.6], n = 3); cohorts 2022: -5.0, 2023: +3.1, 2024: +19.9; pooled post +6.2 (t 2.14 firm / 0.84 two-way, wild p 0.631); post − pre +2.4 (t 0.78 / 0.36, MDE 8.5); permutation p 0.31; sup-Wald p 0.59; post FF6 alpha -0.3 %/yr |
27 28 | Pre / post, S&P 500 members (winsorized) | +3.8 → +13.2; cohorts 2022: +14.8, 2023: -7.1, 2024: +32.0; pooled post +13.3 (t 2.39 / 1.15); post Sharpe 0.69 |
modified paper/main.pdf +0 −0

Binary file not shown.

modified paper/main.tex +5 −12
@@ -96,19 +96,12 @@
96 96 \sisetup{group-minimum-digits=4, group-separator={,}}
97 97 \newcommand{\tstat}[1]{{\footnotesize(\textit{t}=#1)}}
98 98 \newcommand{\tablelegend}[1]{\par\vspace{0.25em}\begin{minipage}{\linewidth}\footnotesize\textit{Notes:} #1\end{minipage}}
99 −% Generated table fragments carry their own \footnotesize notes paragraph after the tabular.
100 −% Scaling the whole fragment with \adjustbox would put the notes on one scaled line, so every
101 −% tabular of the document is wrapped, by an etoolbox hook, in adjustbox{max width=\textwidth}:
102 −% a tabular wider than the text block is shrunk, a narrower one is untouched, and the notes
103 −% paragraph is typeset at \textwidth. \widetable is kept as a marker for the fragments that
104 −% needed scaling before the hook existed.
105 −% The generated fragments wrap each tabular in a threeparttable (which redefines \tabular, so a
106 −% hook on tabular would be bypassed); the hook is therefore attached to threeparttable. A fragment
107 −% whose natural width exceeds \textwidth is shrunk with its notes (the exhibits agent is asked to
108 −% narrow such fragments at generation; the hook keeps the build free of overfull boxes meanwhile).
99 +% Generated table fragments (scripts/r3k_20_tables_v2.py) are self-contained threeparttable floats
100 +% whose natural width fits the text block (34/34 in the Stage E width scan), so no scaling hook is
101 +% needed: the threeparttable -> adjustbox{max width=\textwidth} hook of Stage E was removed at
102 +% Stage F (NB-6) with the build unchanged (0 overfull boxes > 5 pt). \widetable is kept as an
103 +% inert marker for legacy fragments.
109 104 \usepackage{etoolbox}
110 −\BeforeBeginEnvironment{threeparttable}{\begin{adjustbox}{max width=\textwidth}}
111 −\AfterEndEnvironment{threeparttable}{\end{adjustbox}}
112 105 \newcommand{\widetable}[1]{#1}
113 106
114 107 % --- Misc ---
modified paper/sections/data.tex +8 −4
@@ -60,7 +60,7 @@ where $\mathbf e_c$ is the embedding of the chunk. Because similarity is compute
60 60 space, a passage on ``deploying neural networks to forecast demand'' scores highly on AI adoption
61 61 even though it shares no keyword with the anchor; in a probe of written sentences, paraphrases
62 62 without any AI keyword score \mProbeParaphraseAdopt{} on the adoption anchor, above the
63 −\mProbeParaphrasePct{}th percentile of corpus chunks. The eight concepts comprise three AI
63 +\mProbeParaphrasePctOrd{} percentile of corpus chunks. The eight concepts comprise three AI
64 64 facets---\emph{AI adoption}, \emph{AI infrastructure} and \emph{AI disruption risk}---and five
65 65 placebos: \emph{ESG commitment}, \emph{climate risk}, \emph{cybersecurity},
66 66 \emph{automation/labour} and \emph{macro risk}. Two features of the anchors matter for
@@ -195,8 +195,12 @@ them Apple, Microsoft and Nvidia in 2022--24), the share of firm-years without a
195 195 falls from \mShareNoCapVone\% to \mShareNoCapVtwo\%, and \msizeGroupChange\% of firm-years change
196 196 size group. The value-weighted exhibits of the first version were single-stock artefacts---the
197 197 largest weight in a cohort-by-quintile cell was \mvwMaxWeightVone\%---and are replaced
198 −(\cref{subsec:sorts}); the largest weight is now \mvwMaxWeight\%, which is genuine mega-cap
199 −dominance, so a weight-capped variant is reported beside the value-weighted rows. Daily prices
198 +(\cref{subsec:sorts}); in the composite sort's own cells the largest weight is now
199 +\mvwMaxWeightCompos\% (\mvwMaxWeightComposCell) and no cell exceeds 25\%, and the largest weight
200 +across all nine value-weighted sorts is \mvwMaxWeightAll\%, in the \mvwMaxWeightAllSort{} sort
201 +(\mvwMaxWeightAllCell), with \mvwCellsAboveAllDistinct{} cells above 25\%, all of them in the
202 +\mvwSortsAboveAll{} sorts; this is genuine mega-cap dominance, not a data error, so a
203 +weight-capped variant is reported beside the value-weighted rows. Daily prices
200 204 from January 2015 to June 2024 are used for the event study and its placebo launches, with IWV as
201 205 the market index and the daily Fama--French factors for the calendar-time benchmark.
202 206
@@ -271,7 +275,7 @@ design compares each firm to its contemporaneous peers and removes both facts.
271 275 enterprise-technology and software firms, data-intensive services and industrial firms, but also
272 276 two electric utilities (IDACORP, rank \mIdaRank, and Hawaiian Electric, rank \mHeRank), whose
273 277 filings describe data-center load growth and generation capacity rather than AI. Utilities are
274 −the sector with the \mUtilSectorRank{}nd-highest mean composite (\mSectorZUtil{} standard
278 +the sector with the \mUtilSectorRankOrd{}-highest mean composite (\mSectorZUtil{} standard
275 279 deviations, after Information Technology at \mSectorZIT) although \mKwUtilShareZero\% of their
276 280 firm-years contain no core AI term; \cref{subsec:construct} returns to this.
277 281
modified paper/sections/literature.tex +2 −2
@@ -45,7 +45,7 @@ than assumes. The first version of this paper claimed that the embedding separat
45 45 currently use artificial intelligence'' from ``we are an artificial-intelligence leader''; a
46 46 direct probe shows that it does so only in part---negated sentences score lower than affirmative
47 47 ones (\mProbeNegated{} against \mProbeAffirm{} on the adoption anchor) but still above the
48 −\mProbeNegatedPct{}th percentile of corpus chunks---so the claim is withdrawn. Sentence encoders
48 +\mProbeNegatedPctOrd{} percentile of corpus chunks---so the claim is withdrawn. Sentence encoders
49 49 of the MiniLM class are known to be weakly sensitive to negation, and the measure should be read
50 50 as topical proximity to AI vocabulary rather than as a stance. The literature also warns that
51 51 text-based measures inherit the composition of the corpus they are computed on
@@ -75,7 +75,7 @@ investment from job postings and r\'esum\'es and document faster growth among AI
75 75 \citet{acemoglu2022aijobs}, \citet{webb2020} and \citet{hampole2025ai} measure the exposure of
76 76 occupations, establishments and firms' workforces to AI; \citet{acemoglu2020wrong} study the
77 77 labour-market effects of industrial robots. Three papers of 2026 are the closest to ours and were
78 −absent from the first version. \citet{eisfeldt2026generative} construct a firm-level measure of
78 +absent from the first version. \citet{eisfeldt2026generative}, first circulated as \citet{eisfeldt2023generative}, construct a firm-level measure of
79 79 \emph{generative}-AI exposure from the task content of occupations, show that a long--short
80 80 portfolio on that measure earned large returns in the two weeks after ChatGPT's release, and
81 81 separate this workforce exposure from firms' \emph{product} exposure to AI. \citet{basu2026aiexposure}
modified paper/sections/macros_stageE.tex +12 −0
@@ -57,6 +57,18 @@
57 57 \newcommand{\mHeRank}{11}
58 58 \newcommand{\mHeRankStrip}{21}
59 59 \newcommand{\mHeRankCore}{63}
60 +\newcommand{\mIdaRankOrd}{4th}
61 +\newcommand{\mIdaRankStripOrd}{4th}
62 +\newcommand{\mIdaRankCoreOrd}{7th}
63 +\newcommand{\mHeRankOrd}{11th}
64 +\newcommand{\mHeRankStripOrd}{21st}
65 +\newcommand{\mHeRankCoreOrd}{63rd}
66 +\newcommand{\mUtilSectorRankOrd}{2nd}
67 +\newcommand{\mProbeNegatedPctOrd}{99.8th}
68 +\newcommand{\mProbeUtilityPctOrd}{98.1th}
69 +\newcommand{\mProbeParaphrasePctOrd}{99.8th}
70 +\newcommand{\mProbeCyberPctOrd}{94.0th}
71 +\newcommand{\mProbeUnrelatedPctOrd}{43rd}
60 72 \newcommand{\mTopThirtyUtilOriginal}{3}
61 73 \newcommand{\mTopThirtyUtilCore}{2}
62 74 \newcommand{\mTopThirtyOverlapStrip}{22}
modified paper/sections/results.tex +1 −1
@@ -132,7 +132,7 @@ rises after the launch, the broad-universe series does not. The pooled slope-cha
132 132 cohort spread of the sample is 2018. Three tests ask whether 2022 is a special break date. The
133 133 illustrative placebo of the first version (\cref{fig:placebobreaks}), which computes the
134 134 post-minus-pre difference for each candidate first post-cohort from 2013 to 2022, ranks 2022
135 −\mplaceboRankW{}th of \mplaceboN{}; because its windows are nested it is not a test. The exact
135 +\mplaceboRankWOrd{} of \mplaceboN{}; because its windows are nested it is not a test. The exact
136 136 permutation of the 2022--24 block against all \mPermSubsets{} three-cohort subsets gives a
137 137 one-sided $p$-value of \mpermPW{} (\mpermP{} on raw returns), and \mContigAboveW{} of the
138 138 \mContigN{} contiguous three-cohort blocks exceed the 2022--24 block (\mContigAboveRaw{} on raw
modified paper/sections/robustness.tex +5 −5
@@ -240,7 +240,7 @@ eleven sectors (\mSectorZUtil{} standard deviations) with a keyword correlation
240 240 \mKwUtilSpearman{} and \mKwUtilShareZero\% of firm-years without any AI term, and they are
241 241 \mUtilShareQfive\% of top-quintile firm-years against \mUtilShareAll\% of the sample; the probe
242 242 confirms that sentences about data-center load growth and generation capacity score
243 −\mProbeUtilInfra{} on the infrastructure anchor, above the \mProbeUtilityPct{}th percentile of
243 +\mProbeUtilInfra{} on the infrastructure anchor, above the \mProbeUtilityPctOrd{} percentile of
244 244 corpus chunks. Excluding Utilities and Energy leaves the composite spread at \mExUtilFullW{}
245 245 points ($t=\mExUtilFullWT$). Two alternative anchor sets were scored against the stored
246 246 embeddings (no corpus re-embedding): anchors stripped of the cybersecurity and automation
@@ -248,14 +248,14 @@ vocabulary give a composite with rank correlation \mStripRho{} to the baseline,
248 248 \mStripFullW{} points ($t=\mStripFullWT$) and a Fama--MacBeth slope of \mStripFMW{}
249 249 ($t=\mStripFMWT$); a core-AI adoption anchor gives \mCoreRho, \mCoreFullW{} ($t=\mCoreFullWT$) and
250 250 \mCoreFMW{} ($t=\mCoreFMWT$); a cybersecurity anchor stripped of AI terms gives \mCyberStripFullW{}
251 −($t=\mCyberStripFullWT$). IDACORP ranks \mIdaRank{}th on the baseline composite, \mIdaRankStrip{}th
252 −on the stripped anchors and \mIdaRankCore{}th on the core anchor; Hawaiian Electric \mHeRank{}th,
253 −\mHeRankStrip{}st and \mHeRankCore{}rd. The results are therefore not driven by the shared
251 +($t=\mCyberStripFullWT$). IDACORP ranks \mIdaRankOrd{} on the baseline composite, \mIdaRankStripOrd{}
252 +on the stripped anchors and \mIdaRankCoreOrd{} on the core anchor; Hawaiian Electric \mHeRankOrd{},
253 +\mHeRankStripOrd{} and \mHeRankCoreOrd{}. The results are therefore not driven by the shared
254 254 vocabulary of the anchors, but the construct remains proximity to a technology-and-infrastructure
255 255 register rather than validated AI activity. Finally, the negation probe of \cref{subsec:lit_text}:
256 256 negated AI sentences score \mProbeNegated{} on the adoption anchor against \mProbeAffirm{} for
257 257 affirmative ones, a difference of \mProbeDiff{} that holds for \mProbePairsShare\% of
258 −sentence pairs, but both lie above the \mProbeNegatedPct{}th percentile of corpus chunks, so the
258 +sentence pairs, but both lie above the \mProbeNegatedPctOrd{} percentile of corpus chunks, so the
259 259 score distinguishes topic far better than stance.
260 260
261 261 \ExhibitOnce{tab:constructvalidity}{\input{tables/tab_construct_validity}}
modified paper/tables/macros.tex +16 −0
@@ -12,6 +12,9 @@
12 12 \makeatother
13 13 % ---- Number macros: every value below is read from a result file by scripts/r3k_20_tables_v2.py; legacy names keep the legacy
14 14 % statistic (e.g. \meventT is the Welch t), the robust versions carry suffixes W / CDA / Ttw / CI / MDE / Pwild. ----
15 +\newcommand{\mmaxMadjRetTicker}{GME}
16 +\newcommand{\mmaxMadjRetYear}{2019}
17 +\newcommand{\mmaxMadjRetPct}{+4{,}793}
15 18 \newcommand{\mnObs}{27{,}354}
16 19 \newcommand{\mnFirms}{2{,}375}
17 20 \newcommand{\mnFirmYearsPanel}{32{,}818}
@@ -179,6 +182,8 @@
179 182 \newcommand{\mplaceboRank}{8}
180 183 \newcommand{\mplaceboN}{10}
181 184 \newcommand{\mplaceboRankW}{8}
185 +\newcommand{\mplaceboRankOrd}{8th}
186 +\newcommand{\mplaceboRankWOrd}{8th}
182 187 \newcommand{\mpermP}{0.28}
183 188 \newcommand{\mpermPW}{0.31}
184 189 \newcommand{\mpermRank}{126}
@@ -298,6 +303,17 @@
298 303 \newcommand{\mewQfive}{+23.1}
299 304 \newcommand{\mvwMaxWeight}{36}
300 305 \newcommand{\mvwMaxWeightVone}{100.0}
306 +\newcommand{\mvwMaxWeightAll}{36}
307 +\newcommand{\mvwMaxWeightAllSort}{cybersecurity placebo}
308 +\newcommand{\mvwMaxWeightAllCell}{AAPL, 2023 cohort, quintile 2}
309 +\newcommand{\mvwCellsAboveAll}{18}
310 +\newcommand{\mvwCellsAboveAllDistinct}{9}
311 +\newcommand{\mvwSortsAboveAll}{SPID technology and cybersecurity placebo}
312 +\newcommand{\mvwMaxWeightCompos}{19}
313 +\newcommand{\mvwMaxWeightComposCell}{AAPL, 2011 cohort, quintile 4}
314 +\newcommand{\mvwCellsAboveCompos}{0}
315 +\newcommand{\mvwMaxWeightComposVone}{100.0}
316 +\newcommand{\mvwCellsAboveComposVone}{31}
301 317 \newcommand{\mfeCompos}{-0.07}
302 318 \newcommand{\mfeComposT}{-0.12}
303 319 \newcommand{\mfeAdopt}{-0.21}
modified paper/tables/tab_hygiene.tex +2 −1
@@ -28,7 +28,8 @@ Statistic & \multicolumn{2}{l}{BEFORE (committed v2.0)} & \multicolumn{3}{l}{AFT
28 28 \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Panel: firm-years / firms / no capitalization} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{27{,}468 / 2{,}379 / 2{,}168} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{27{,}354 / 2{,}375 / 308} \\
29 29 \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{log capitalization range (min, max)} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{[$-\infty$, 37.4]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{[15.5, 29.0]} \\
30 30 \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{AAPL capitalization, June 2011 (USD bn; rank)} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{9.3 (rank 166)} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{310 (rank 2)} \\
31 −\multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Max single-firm VW weight in a cohort $\times$ quintile; cells $>$ 25\%} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{100.0\%; 548} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{36\%; 18} \\
31 +\multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Max single-firm VW weight in a cohort $\times$ quintile, all nine VW sorts (raw and wins.\ variants); entries $>$ 25\%} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{100.0\% (AJG); 548} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{36\% (cybersecurity placebo sort, AAPL, 2023 cohort, quintile 2); 18} \\
32 +\multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Max single-firm VW weight in a cohort $\times$ quintile, composite sort; cells $>$ 25\%} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{100.0\% (AJG); 31} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{19\% (AAPL, 2011 cohort, quintile 4); 0} \\
32 33 \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{EW composite Q5$-$Q1, full (pp) [NW $t$]} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{+5.5 [2.41]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{+5.5 [2.40] $\rightarrow$ +3.9 [2.00; plain 1.78], CI [-0.8, +8.6]; median +0.5 [0.29]} \\
33 34 \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{VW composite: Q1 / Q5 / Q5$-$Q1 [NW $t$]} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{+0.4 / +14.3 / +13.9 [2.59]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{+12.1 / +19.4 / +7.4 [2.69] $\rightarrow$ wins.\ +7.3 [2.68]} \\
34 35 \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Fama--MacBeth composite slope (pp/SD) [NW $t$]} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{+3.07 [2.80]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{+2.77 [2.44] $\rightarrow$ +1.88 [2.41; plain 2.51], CI [+0.27, +3.49]} \\
modified paper/tables/tab_sorts.tex +1 −1
@@ -57,6 +57,6 @@ SPID projects (legacy quintiles) & EW, wins. & +16.9 & +17.7 & +18.7 & +18.7 & +
57 57 \bottomrule
58 58 \end{tabular}
59 59 \begin{tablenotes}[flushleft]\scriptsize
60 −\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\ref{tab:spidbinary} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\times$ point-in-time shares); maximum single-firm weight 36\%.
60 +\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\ref{tab:spidbinary} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\times$ point-in-time shares). Maximum single-firm weight in a cohort $\times$ quintile cell: 36\% across the nine VW sorts of this table (cybersecurity placebo sort: AAPL, 2023 cohort, quintile 2; 9 distinct cells above 25\%, all in the SPID technology and cybersecurity placebo sorts) and 19\% in the composite sort (AAPL, 2011 cohort, quintile 4; no cell above 25\%).
61 61 \end{tablenotes}
62 62 \end{threeparttable}}
modified paper/tables/tab_sorts_both.tex +1 −1
@@ -36,6 +36,6 @@ PC1 of three facets & EW, wins. & +16.1 & +16.3 & +17.5 & +19.1 & +21.7 & +5.7 &
36 36 \bottomrule
37 37 \end{tabular}
38 38 \begin{tablenotes}[flushleft]\scriptsize
39 −\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. Signals are standardized within year and within panel. Economic magnitude: the composite EW wins.\ spread of +3.9 pp per year compares with a Q1 mean of +16.4\% per year; the top 1\% of returns contribute +2.5 pp of the raw spread. VW diagnostic: maximum single-firm weight in any cohort $\times$ quintile cell 36\% (committed v2.0 capitalization: 100.0\%); cells above 25\%: 18 (v2.0: 548); the 10\%-capped VW composite spread is +7.2 pp (NW $t$ 2.62). Romano--Wolf adjusted $p$ of the composite EW wins.\ spread within the ten-member family of this table: 0.32 (Table~\ref{tab:mht}). All signals and weightings: Table~\ref{tab:sorts}.
39 +\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. Signals are standardized within year and within panel. Economic magnitude: the composite EW wins.\ spread of +3.9 pp per year compares with a Q1 mean of +16.4\% per year; the top 1\% of returns contribute +2.5 pp of the raw spread. VW diagnostic (single-firm weight in a cohort $\times$ quintile cell): in the composite sort's own cells the maximum is 19\% (AAPL, 2011 cohort, quintile 4) and no cell exceeds 25\%; across all nine full-sample VW sorts of Table~\ref{tab:sorts} the maximum is 36\% (cybersecurity placebo sort: AAPL, 2023 cohort, quintile 2) and 18 entries exceed 25\% (9 distinct cells, each counted once for the raw and once for the winsorized return variant); committed v2.0 capitalization: 100.0\% and 548 entries (274 distinct cells); the 10\%-capped VW composite spread is +7.2 pp (NW $t$ 2.62). Romano--Wolf adjusted $p$ of the composite EW wins.\ spread within the ten-member family of this table: 0.32 (Table~\ref{tab:mht}). All signals and weightings: Table~\ref{tab:sorts}.
40 40 \end{tablenotes}
41 41 \end{threeparttable}}
modified paper/tables/tab_summary.tex +1 −1
@@ -21,6 +21,6 @@ Text chunks per firm-year (count) & 27{,}354 & 330 & 154 & 1 & 68 & 306 & 866 &
21 21 \bottomrule
22 22 \end{tabular}
23 23 \begin{tablenotes}[flushleft]\scriptsize
24 −\item \emph{Notes:} Market capitalization = unadjusted FinData OS close $\times$ point-in-time SEC cover-page shares (log range [15.5, 29.0]); 308 firm-years (1.1\%) have no capitalization and enter the equal-weighted sorts only (the committed v2.0 panel had 2{,}168). Return hygiene: 48 monthly returns above 300\% and 104 firm-year forward returns above 500\% were checked against FinData OS daily bars; 11 monthly and 3 firm-year observations were price splices and are set to missing (Table~\ref{tab:hygiene}), the others are confirmed. The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. Source: data/processed\_r3k\_v2/analysis\_panel\_v2.parquet, results/r3k\_v2/t1\_summary\_stats.csv.
24 +\item \emph{Notes:} Market capitalization = unadjusted FinData OS close $\times$ point-in-time SEC cover-page shares (log range [15.5, 29.0]); 308 firm-years (1.1\%) have no capitalization and enter the equal-weighted sorts only (the committed v2.0 panel had 2{,}168). Return hygiene: 48 monthly returns above 300\% and 104 firm-year forward returns above 500\% were checked against FinData OS daily bars; 11 monthly and 3 firm-year observations were price splices and are set to missing (Table~\ref{tab:hygiene}), the others are confirmed. The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. The maximum of the market-adjusted forward return (47.93, i.e.\ +4{,}793\%) is GME in formation year 2019 (holding window 2020-06-30 to 2021-06-30), also the maximum of the raw return; its second-source verdict is `confirmed' (FinData OS daily bars, 2020-06-30 1.085 $\to$ 2021-06-30 53.53). Source: data/processed\_r3k\_v2/analysis\_panel\_v2.parquet, results/r3k\_v2/t1\_summary\_stats.csv, hygiene\_log.csv.
25 25 \end{tablenotes}
26 26 \end{threeparttable}}
modified results/r3k_v2/inline_numbers.json +16 −0
@@ -1,4 +1,7 @@
1 1 {
2 + "maxMadjRetTicker": "GME",
3 + "maxMadjRetYear": "2019",
4 + "maxMadjRetPct": "+4{,}793",
2 5 "nObs": "27{,}354",
3 6 "nFirms": "2{,}375",
4 7 "nFirmYearsPanel": "32{,}818",
@@ -166,6 +169,8 @@
166 169 "placeboRank": "8",
167 170 "placeboN": "10",
168 171 "placeboRankW": "8",
172 + "placeboRankOrd": "8th",
173 + "placeboRankWOrd": "8th",
169 174 "permP": "0.28",
170 175 "permPW": "0.31",
171 176 "permRank": "126",
@@ -285,6 +290,17 @@
285 290 "ewQfive": "+23.1",
286 291 "vwMaxWeight": "36",
287 292 "vwMaxWeightVone": "100.0",
293 + "vwMaxWeightAll": "36",
294 + "vwMaxWeightAllSort": "cybersecurity placebo",
295 + "vwMaxWeightAllCell": "AAPL, 2023 cohort, quintile 2",
296 + "vwCellsAboveAll": "18",
297 + "vwCellsAboveAllDistinct": "9",
298 + "vwSortsAboveAll": "SPID technology and cybersecurity placebo",
299 + "vwMaxWeightCompos": "19",
300 + "vwMaxWeightComposCell": "AAPL, 2011 cohort, quintile 4",
301 + "vwCellsAboveCompos": "0",
302 + "vwMaxWeightComposVone": "100.0",
303 + "vwCellsAboveComposVone": "31",
288 304 "feCompos": "-0.07",
289 305 "feComposT": "-0.12",
290 306 "feAdopt": "-0.21",
modified scripts/r3k_20_tables_v2.py +34 −6
@@ -36,6 +36,9 @@ def pv(p): return "--" if p is None or (isinstance(p, float) and np.isnan(p)) el
36 36 def pct(x, d=1): return "--" if x is None else f"{x * 100:.{d}f}\\%"
37 37 def br(t): return "--" if t is None or (isinstance(t, float) and np.isnan(t)) else f"[{t:.2f}]" # t in brackets
38 38 def tex(s): return str(s).replace("&", "\\&").replace("%", "\\%").replace("_", "\\_")
39 +def ordinal(x):
40 + """Rank -> '4th', '21st', '63rd', '11th' (Stage F, NB-4: the suffix is emitted with the rank so the two cannot diverge)."""
41 + k = int(x); return f"{k}{'th' if 10 <= k % 100 <= 20 else {1: 'st', 2: 'nd', 3: 'rd'}.get(k % 10, 'th')}"
39 42 MAC = {}; EXHIBITS = []
40 43 TABLE_NOTE_SE = ("Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; "
41 44 "95\\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns "
@@ -71,12 +74,19 @@ lab = {"ai_adoption_wmean": ("AI adoption score (section-weighted cosine)", 4),
71 74 body = ""
72 75 for k, (l, d) in lab.items():
73 76 r = s.loc[k]; body += f"{l} & {n(r['count'])} & {r['mean']:.{d}f} & {r['std']:.{d}f} & {r['min']:.{d}f} & {r['1%']:.{d}f} & {r['50%']:.{d}f} & {r['99%']:.{d}f} & {r['max']:.{d}f} \\\\\n"
77 +# Stage F (NB-11): the Max column's extreme market-adjusted forward return is named (firm, formation year, holding window, second-source verdict).
78 +_mx = df.loc[df.fwd_ret_mktadj.idxmax()]; _hl0 = pd.read_csv(f"{T}/hygiene_log.csv"); _hr = _hl0[(_hl0.kind == "firm_year_fwd") & (_hl0.ticker == _mx.ticker) & (_hl0.fiscal_year == _mx.fiscal_year)]
79 +assert len(_hr) == 1, ("maximum forward return without a hygiene verdict", _mx.ticker, _mx.fiscal_year); _hr = _hr.iloc[0]
80 +_mxpct = f"{_mx.fwd_ret_mktadj * 100:+,.0f}".replace(",", "{,}")
81 +MAXRET_NOTE = (f"The maximum of the market-adjusted forward return ({_mx.fwd_ret_mktadj:.2f}, i.e.\\ {_mxpct}\\%) is {tex(_mx.ticker)} in formation year {int(_mx.fiscal_year)} (holding window {_hr.date.replace('..', ' to ')})"
82 + + (", also the maximum of the raw return" if df.fwd_ret.idxmax() == df.fwd_ret_mktadj.idxmax() else "") + f"; its second-source verdict is `{_hr.verdict.replace('_', ' ')}' (FinData OS daily bars, {tex(_hr.note).replace('->', '$\\to$')}). ")
83 +MAC["maxMadjRetTicker"] = tex(_mx.ticker); MAC["maxMadjRetYear"] = str(int(_mx.fiscal_year)); MAC["maxMadjRetPct"] = _mxpct
74 84 table("tab_summary", "tab:summ", "Summary statistics of the estimation sample.",
75 85 f"Firm-years with a valid twelve-month forward return, formation cohorts {YRS} ($N$ = {NOBS} firm-years, {NFIRMS} firms; iShares Russell~3000 holdings of October 2026 plus the S\\&P~500). Semantic scores are section-weighted mean cosine similarities of the firm-year's filing chunks to the anchor concept; returns are simple buy-and-hold fractions.",
76 86 "lrrrrrrrr", ["Variable & $N$ & Mean & SD & Min & P1 & Median & P99 & Max"], body,
77 87 f"Market capitalization = unadjusted FinData OS close $\\times$ point-in-time SEC cover-page shares (log range [{MD['log_cap_min_max_v2'][0]:.1f}, {MD['log_cap_min_max_v2'][1]:.1f}]); {n(META['n_est_without_cap'])} firm-years ({MD['share_est_without_cap_v2'] * 100:.1f}\\%) have no capitalization and enter the equal-weighted sorts only (the committed v2.0 panel had {n(META['n_est_without_cap_v1'])}). "
78 88 f"Return hygiene: {HY['n_flagged_monthly']} monthly returns above 300\\% and {HY['n_flagged_firm_years']} firm-year forward returns above 500\\% were checked against FinData OS daily bars; {HY['verdicts_monthly'].get('series_break', 0)} monthly and {HY['verdicts_firm_year'].get('series_break', 0)} firm-year observations were price splices and are set to missing (Table~\\ref{{tab:hygiene}}), the others are confirmed. "
79 − f"The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. Source: data/processed\\_r3k\\_v2/analysis\\_panel\\_v2.parquet, results/r3k\\_v2/t1\\_summary\\_stats.csv.",
89 + f"The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. " + MAXRET_NOTE + f"Source: data/processed\\_r3k\\_v2/analysis\\_panel\\_v2.parquet, results/r3k\\_v2/t1\\_summary\\_stats.csv, hygiene\\_log.csv.",
80 90 size="\\scriptsize", placement="main", sources="results/r3k_v2/t1_summary_stats.csv, panel_meta_v2.json, mktcap_diag.json, hygiene_summary.json")
81 91 # ================================================================ T5 quintile sorts, both universes (main) ====
82 92 SIGL = [("AI_ADOPT", "AI adoption"), ("AI_INFRA", "AI infrastructure"), ("AI_RISK", "AI disruption risk"), ("AI_COMPOS", "AI composite"), ("AI_PC1", "PC1 of three facets"), ("AI_formw", "Equal-form composite"), ("AI_10k", "10-K composite"), ("SPID_TECH", "SPID projects (legacy quintiles)")]
@@ -94,11 +104,28 @@ for sname, lab_ in [("r3k", "Panel A: Russell 3000 universe"), ("sp500", "Panel
94 104 for i, sfx in enumerate(rows): body += sort_row(SM["sorts"], k, l, sfx, i == 0)
95 105 if sname == "r3k": body += "\\midrule\n"
96 106 VD = R["vw_diag"]
107 +# Stage F (NB-1): results.json vw_diag mixes two universes -- max_single_firm_weight_v2 / n_cells_above_25pct_v2 are taken over EVERY
108 +# full-sample VW sort of the nine signals (r3k_15 regex "_VW$|_VW_w$", raw and winsorized variants counted separately), while
109 +# worst_cells_v2 is the composite sort alone. Both statistics are made explicit here from results/r3k_v2/vw_weights_diag.csv (written by
110 +# r3k_15 beside results.json), the worst cell of each universe is named, and the all-sorts figures are asserted equal to vw_diag.
111 +vwd_ = pd.read_csv(f"{T}/vw_weights_diag.csv"); SIGNAME_VW = {"AI_ADOPT": "AI adoption", "AI_INFRA": "AI infrastructure", "AI_RISK": "AI disruption risk", "AI_COMPOS": "AI composite", "AI_PC1": "PC1", "AI_formw": "equal-form composite", "AI_10k": "10-K composite", "SPID_TECH": "SPID technology", "CYBER": "cybersecurity placebo"}
112 +def _vw_universe(suffix_re, prefix_ok=("r3k_", "sp500_")): return vwd_[vwd_["sort"].str.contains(suffix_re, regex=True) & ~vwd_["sort"].str.startswith(prefix_ok)]
113 +def _vw_stats(d):
114 + w = d.loc[d.max_weight.idxmax()]; base = re.sub(r"_VW(_v1)?(_w)?$", "", w["sort"]); above = d[d.max_weight > 0.25]
115 + return {"max": float(w.max_weight), "sort": SIGNAME_VW.get(base, base), "ticker": str(w.max_ticker), "cohort": int(w.cohort), "q": int(w.q), "n_above": int(len(above)), "sorts_above": sorted({SIGNAME_VW.get(re.sub(r"_VW(_v1)?(_w)?$", "", x), x) for x in above["sort"]}), "n_above_distinct": int(above.assign(base=above["sort"].str.replace(r"_w$", "", regex=True)).drop_duplicates(["base", "cohort", "q"]).shape[0])}
116 +VWALL = _vw_stats(_vw_universe("_VW$|_VW_w$")); VWCOMP = _vw_stats(vwd_[vwd_["sort"] == "AI_COMPOS_VW"]); VWALL1 = _vw_stats(_vw_universe("_VW_v1$|_VW_v1_w$")); VWCOMP1 = _vw_stats(vwd_[vwd_["sort"] == "AI_COMPOS_VW_v1"])
117 +assert abs(VWALL["max"] - VD["max_single_firm_weight_v2"]) < 1e-6 and VWALL["n_above"] == VD["n_cells_above_25pct_v2"], ("vw_weights_diag.csv and results.json vw_diag disagree", VWALL, VD)
118 +assert abs(VWALL1["max"] - VD["max_single_firm_weight_v1"]) < 1e-6 and VWALL1["n_above"] == VD["n_cells_above_25pct_v1"], ("v1 universe", VWALL1, VD)
119 +def vwcell(x): return f"{x['ticker']}, {x['cohort']} cohort, quintile {x['q']}"
120 +VW_NOTE = (f"VW diagnostic (single-firm weight in a cohort $\\times$ quintile cell): in the composite sort's own cells the maximum is {VWCOMP['max'] * 100:.0f}\\% ({vwcell(VWCOMP)}) and "
121 + f"{'no cell exceeds' if VWCOMP['n_above'] == 0 else str(VWCOMP['n_above']) + ' cells exceed'} 25\\%; across all nine full-sample VW sorts of Table~\\ref{{tab:sorts}} the maximum is {VWALL['max'] * 100:.0f}\\% "
122 + f"({VWALL['sort']} sort: {vwcell(VWALL)}) and {VWALL['n_above']} entries exceed 25\\% ({VWALL['n_above_distinct']} distinct cells, each counted once for the raw and once for the winsorized return variant); "
123 + f"committed v2.0 capitalization: {VD['max_single_firm_weight_v1'] * 100:.1f}\\% and {VD['n_cells_above_25pct_v1']} entries ({VWALL1['n_above_distinct']} distinct cells)")
97 124 table("tab_sorts_both", "tab:sortsboth", "Quintile sorts on AI language in the Russell~3000 universe and among S\\&P~500 members.",
98 125 f"Mean forward twelve-month return (percent per year) of the within-cohort quintiles of each signal, averaged over the {R['sorts']['AI_COMPOS_EW_w']['n_years']} formation cohorts {YRS}, with the Q5$-$Q1 spread (percentage points), its Newey--West and i.i.d.\\ $t$-statistics and the 95\\% confidence interval over cohorts. EW = equal-weighted; VW = value-weighted on the corrected capitalization. The headline row of each signal is the within-year winsorized return; for the composite the raw and median versions show the sensitivity of the sort to the return tail.",
99 126 "llrrrrrrrrr", HDR5, body,
100 127 TABLE_NOTE_SE + f" Signals are standardized within year and within panel. Economic magnitude: the composite EW wins.\\ spread of {pp(R['samples']['r3k']['sorts']['AI_COMPOS_EW_w']['Q5_minus_Q1'])} pp per year compares with a Q1 mean of {pp(R['samples']['r3k']['sorts']['AI_COMPOS_EW_w']['Q1'])}\\% per year; the top 1\\% of returns contribute {pp(R['sorts']['AI_COMPOS_EW']['top1pct_contribution'])} pp of the raw spread. "
101 − f"VW diagnostic: maximum single-firm weight in any cohort $\\times$ quintile cell {VD['max_single_firm_weight_v2'] * 100:.0f}\\% (committed v2.0 capitalization: {VD['max_single_firm_weight_v1'] * 100:.1f}\\%); cells above 25\\%: {VD['n_cells_above_25pct_v2']} (v2.0: {VD['n_cells_above_25pct_v1']}); the 10\\%-capped VW composite spread is {pp(R['sorts']['AI_COMPOS_VWcap10_w']['Q5_minus_Q1'])} pp (NW $t$ {tv(R['sorts']['AI_COMPOS_VWcap10_w']['t_stat'])}). "
128 + + VW_NOTE + f"; the 10\\%-capped VW composite spread is {pp(R['sorts']['AI_COMPOS_VWcap10_w']['Q5_minus_Q1'])} pp (NW $t$ {tv(R['sorts']['AI_COMPOS_VWcap10_w']['t_stat'])}). "
102 129 f"Romano--Wolf adjusted $p$ of the composite EW wins.\\ spread within the ten-member family of this table: {dict(zip(R2['mht']['sorts_full_w']['members'], R2['mht']['sorts_full_w']['p_romano_wolf']))['AI_COMPOS_EW']:.2f} (Table~\\ref{{tab:mht}}). All signals and weightings: Table~\\ref{{tab:sorts}}.",
103 130 size="\\scriptsize", colsep="3pt", placement="main", sources="results/r3k_v2/results.json (samples.*.sorts, vw_diag), results2.json (mht)")
104 131 # ================================================================ appendix: full sorts (all signals / weightings, Russell 3000) ====
@@ -109,7 +136,7 @@ for k, l in SIGL:
109 136 table("tab_sorts", "tab:sorts", "Quintile sorts on every signal and weighting, Russell~3000 universe.",
110 137 f"Companion to Table~\\ref{{tab:sortsboth}}: mean forward twelve-month return (percent per year) by within-cohort quintile for all eight signals, equal-weighted (winsorized, raw, median) and value-weighted on the corrected capitalization (unrestricted and with single-firm weights capped at 10\\%), averaged over the {R['sorts']['AI_COMPOS_EW']['n_years']} cohorts {YRS} ($N$ = {NOBS} firm-years).",
111 138 "llrrrrrrrrr", HDR5, body,
112 − TABLE_NOTE_SE + f" The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\\ref{{tab:spidbinary}} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\\times$ point-in-time shares); maximum single-firm weight {VD['max_single_firm_weight_v2'] * 100:.0f}\\%.",
139 + TABLE_NOTE_SE + f" The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\\ref{{tab:spidbinary}} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\\times$ point-in-time shares). Maximum single-firm weight in a cohort $\\times$ quintile cell: {VWALL['max'] * 100:.0f}\\% across the nine VW sorts of this table ({VWALL['sort']} sort: {vwcell(VWALL)}; {VWALL['n_above_distinct']} distinct cells above 25\\%, all in the {' and '.join(VWALL['sorts_above'])} sorts) and {VWCOMP['max'] * 100:.0f}\\% in the composite sort ({vwcell(VWCOMP)}; {'no cell' if VWCOMP['n_above'] == 0 else str(VWCOMP['n_above']) + ' cells'} above 25\\%).",
113 140 size="\\scriptsize", colsep="3pt", placement="appendix", sources="results/r3k_v2/results.json (sorts, vw_diag)")
114 141 # ================================================================ T6 monthly long-short alphas, both universes (main) ====
115 142 body = ""
@@ -556,7 +583,8 @@ if RB:
556 583 def lr(v): return "$-\\infty$" if v is None or (isinstance(v, float) and np.isinf(v)) else f"{v:.1f}"
557 584 body += ba("log capitalization range (min, max)", f"[{lr(MD['log_cap_min_max_v1'][0])}, {lr(MD['log_cap_min_max_v1'][1])}]", f"[{lr(MD['log_cap_min_max_v2'][0])}, {lr(MD['log_cap_min_max_v2'][1])}]")
558 585 body += ba("AAPL capitalization, June 2011 (USD bn; rank)", f"{MD['AAPL_cohort2010_cap_v1_usd'] / 1e9:.1f} (rank {MD['AAPL_cohort2010_rank_v1']:.0f})", f"{MD['AAPL_cohort2010_cap_v2_usd'] / 1e9:.0f} (rank {MD['AAPL_cohort2010_rank_v2']:.0f})")
559 − body += ba("Max single-firm VW weight in a cohort $\\times$ quintile; cells $>$ 25\\%", f"{VD['max_single_firm_weight_v1'] * 100:.1f}\\%; {VD['n_cells_above_25pct_v1']}", f"{VD['max_single_firm_weight_v2'] * 100:.0f}\\%; {VD['n_cells_above_25pct_v2']}")
586 + body += ba("Max single-firm VW weight in a cohort $\\times$ quintile, all nine VW sorts (raw and wins.\\ variants); entries $>$ 25\\%", f"{VD['max_single_firm_weight_v1'] * 100:.1f}\\% ({VWALL1['ticker']}); {VD['n_cells_above_25pct_v1']}", f"{VD['max_single_firm_weight_v2'] * 100:.0f}\\% ({VWALL['sort']} sort, {vwcell(VWALL)}); {VD['n_cells_above_25pct_v2']}")
587 + body += ba("Max single-firm VW weight in a cohort $\\times$ quintile, composite sort; cells $>$ 25\\%", f"{VWCOMP1['max'] * 100:.1f}\\% ({VWCOMP1['ticker']}); {VWCOMP1['n_above']}", f"{VWCOMP['max'] * 100:.0f}\\% ({vwcell(VWCOMP)}); {VWCOMP['n_above']}")
560 588 body += ba("EW composite Q5$-$Q1, full (pp) [NW $t$]", f"{pp(S0['AI_COMPOS_EW']['Q5_minus_Q1'])} [{tv(S0['AI_COMPOS_EW']['t_stat'])}]", f"{pp(S['AI_COMPOS_EW']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_EW']['t_stat'])}] $\\rightarrow$ {pp(S['AI_COMPOS_EW_w']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_EW_w']['t_stat'])}; plain {tv(S['AI_COMPOS_EW_w']['t_plain'])}], CI {ci(S['AI_COMPOS_EW_w']['ci95'])}; median {pp(S['AI_COMPOS_EW_med']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_EW_med']['t_stat'])}]")
561 589 body += ba("VW composite: Q1 / Q5 / Q5$-$Q1 [NW $t$]", f"{pp(S0['AI_COMPOS_VW']['Q1'])} / {pp(S0['AI_COMPOS_VW']['Q5'])} / {pp(S0['AI_COMPOS_VW']['Q5_minus_Q1'])} [{tv(S0['AI_COMPOS_VW']['t_stat'])}]", f"{pp(S['AI_COMPOS_VW']['Q1'])} / {pp(S['AI_COMPOS_VW']['Q5'])} / {pp(S['AI_COMPOS_VW']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_VW']['t_stat'])}] $\\rightarrow$ wins.\\ {pp(S['AI_COMPOS_VW_w']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_VW_w']['t_stat'])}]")
562 590 body += ba("Fama--MacBeth composite slope (pp/SD) [NW $t$]", f"{FM0['mean_coef'] * 100:+.2f} [{tv(FM0['t'])}]", f"{FM['compos']['z_AI_COMPOS']['mean_coef'] * 100:+.2f} [{tv(FM['compos']['z_AI_COMPOS']['t'])}] $\\rightarrow$ {FM['compos_w']['z_AI_COMPOS']['mean_coef'] * 100:+.2f} [{tv(FM['compos_w']['z_AI_COMPOS']['t'])}; plain {tv(FM['compos_w']['z_AI_COMPOS']['t_plain'])}], CI {ci(FM['compos_w']['z_AI_COMPOS']['ci95'], 2)}")
@@ -621,7 +649,7 @@ MAC.update({"nObs": n(R["n_obs"]), "nFirms": n(R["n_firms"]), "nFirmYearsPanel":
621 649 "sectNeuPost": pp(SN["post"]["mean"]), "sectNeuPostT": tv(SN["post"]["t"]), "sectNeuFull": pp(SN["full"]["mean"]), "sectNeuFullT": tv(SN["full"]["t"]), "sectNeuFullW": pp(SN["full_w"]["mean"]), "sectNeuFullWT": tv(SN["full_w"]["t"]), "residSortFullW": pp(RSd["full_w"]["mean"]), "residSortFullWT": tv(RSd["full_w"]["t"]), "techBetaPost": f"{M['post'].get('tech_beta', float('nan')):.2f}", "techBetaFull": f"{M['full'].get('tech_beta', float('nan')):.2f}", "ffsixTechFull": f1(M["full"].get("ff6_tech_alpha_pct", float("nan"))), "ffsixTechFullT": f"{M['full'].get('ff6_tech_t', float('nan')):.2f}", "sharpeFull": f"{M['full']['sharpe']:.2f}", "meanLSFull": f1(M["full"]["mean_ann_pct"]), "hacFull": f"{M['full']['hac_t']:.2f}", "ffsixTechPost": f1(M["post"].get("ff6_tech_alpha_pct", float("nan"))), "ffsixTechPostT": f"{M['post'].get('ff6_tech_t', float('nan')):.2f}",
622 650 "fmPostSector": f"{Lc_['fm_all_raw_sector_post']['slope_pct']:+.2f}", "fmPostSectorT": tv(Lc_["fm_all_raw_sector_post"]["t_nw"]), "fmPost": f"{Lc_['fm_all_raw_nosector_post']['slope_pct']:+.2f}", "fmPostT": tv(Lc_["fm_all_raw_nosector_post"]["t_nw"]), "syPost": f"{Lc_['pooled_all_post']['sector_year_fe']['coef_pct']:+.2f}", "syPostT": f"{Lc_['pooled_all_post']['sector_year_fe']['t_firm']:.2f}", "syPostTtw": tv(Lc_['pooled_all_post']['sector_year_fe']['t_twoway']),
623 651 "fmFullSector": f"{Lc_['fm_all_raw_sector_full']['slope_pct']:+.2f}", "fmFullSectorT": tv(Lc_["fm_all_raw_sector_full"]["t_nw"]), "fmFullSectorTplain": tv(Lc_["fm_all_raw_sector_full"]["t_plain"]), "fmFullSectorW": f"{Lc_['fm_all_w_sector_full']['slope_pct']:+.2f}", "fmFullSectorWT": tv(Lc_["fm_all_w_sector_full"]["t_nw"]), "fmFullSectorWTplain": tv(Lc_["fm_all_w_sector_full"]["t_plain"]), "fmFullSectorExEnergyW": f"{Lc_['fm_ex_energy_w_sector_full']['slope_pct']:+.2f}", "fmFullSectorExEnergyWT": tv(Lc_["fm_ex_energy_w_sector_full"]["t_nw"]), "fixedBcv": f"{FB:.2f}", "fmPre": f"{Lc_['fm_all_raw_nosector_pre']['slope_pct']:+.2f}", "fmPreT": tv(Lc_["fm_all_raw_nosector_pre"]["t_nw"]), "fmFull": f"{Lc_['fm_all_raw_nosector_full']['slope_pct']:+.2f}", "fmFullT": tv(Lc_["fm_all_raw_nosector_full"]["t_nw"]), "syFull": f"{Lc_['pooled_all_full']['sector_year_fe']['coef_pct']:+.2f}", "syFullT": f"{Lc_['pooled_all_full']['sector_year_fe']['t_firm']:.2f}", "syFullTtw": tv(Lc_['pooled_all_full']['sector_year_fe']['t_twoway']), "syFullPwild": pv(Lc_["pooled_all_full"]["sector_year_fe"].get("p_wild_webb")),
624 − "placeboRank": str(PB["rank_of_2022"]), "placeboN": str(PB["n_candidates"]), "placeboRankW": str(PB["winsorized"]["rank_of_2022"]), "permP": f"{PB['permutation']['p_one_sided']:.2f}", "permPW": f"{PB['winsorized']['permutation']['p_one_sided']:.2f}", "permRank": str(PB["permutation"]["rank_among_subsets"]), "supWald": f"{PB['sup_wald']['sup_wald']:.2f}", "supWaldP": f"{PB['sup_wald']['p_simulated']:.2f}", "supWaldW": f"{PB['winsorized']['sup_wald']['sup_wald']:.2f}", "supWaldWP": f"{PB['winsorized']['sup_wald']['p_simulated']:.2f}", "placeboBest": max(PB["diff_post_minus_pre_pct_by_break_year"], key=PB["diff_post_minus_pre_pct_by_break_year"].get), "placeboDiffTwentyTwo": f1(PB["diff_post_minus_pre_pct_by_break_year"]["2022"]),
652 + "placeboRank": str(PB["rank_of_2022"]), "placeboN": str(PB["n_candidates"]), "placeboRankW": str(PB["winsorized"]["rank_of_2022"]), "placeboRankOrd": ordinal(PB["rank_of_2022"]), "placeboRankWOrd": ordinal(PB["winsorized"]["rank_of_2022"]), "permP": f"{PB['permutation']['p_one_sided']:.2f}", "permPW": f"{PB['winsorized']['permutation']['p_one_sided']:.2f}", "permRank": str(PB["permutation"]["rank_among_subsets"]), "supWald": f"{PB['sup_wald']['sup_wald']:.2f}", "supWaldP": f"{PB['sup_wald']['p_simulated']:.2f}", "supWaldW": f"{PB['winsorized']['sup_wald']['sup_wald']:.2f}", "supWaldWP": f"{PB['winsorized']['sup_wald']['p_simulated']:.2f}", "placeboBest": max(PB["diff_post_minus_pre_pct_by_break_year"], key=PB["diff_post_minus_pre_pct_by_break_year"].get), "placeboDiffTwentyTwo": f1(PB["diff_post_minus_pre_pct_by_break_year"]["2022"]),
625 653 "didCont": f"{Dd['continuous']['coef'] * 100:+.2f}", "didContT": f"{Dd['continuous']['t']:.2f}", "didBin": f"{Dd['binary']['coef'] * 100:+.2f}", "didBinT": f"{Dd['binary']['t']:.2f}",
626 654 "evtTwentyTwo": f1(ET[2022]["abs_slope"] * 100), "evtTwentyFour": f1(ET[2024]["abs_slope"] * 100), "evtEighteen": f1(ET[2018]["abs_slope"] * 100), "evtDiffTwentyTwo": f1(ET[2022]["diff_from_ref"] * 100), "evtDiffTwentyThree": f1(ET[2023]["diff_from_ref"] * 100), "evtDiffTwentyFour": f1(ET[2024]["diff_from_ref"] * 100), "evtDiffTwentyTwoSE": f"{ET[2022]['diff_se'] * 100:.1f}", "evtBase": f1(R2["did_eventtime"]["base_slope_ref_year"] * 100), "evtTrailTwentyTwo": f1(ETt[2022]["diff_from_ref"] * 100), "evtTrailTwentyFour": f1(ETt[2024]["diff_from_ref"] * 100),
627 655 "tenkPost": pp(Nf["10k"]["post"]["mean"]), "tenkPostT": tv(Nf["10k"]["post"]["t"]), "tenkPre": pp(Nf["10k"]["pre"]["mean"]), "tenqPost": pp(Nf["10q"]["post"]["mean"]), "eightkPost": pp(Nf["8k"]["post"]["mean"]), "corrTenK": f"{Nf['10k']['corr_with_baseline']:.2f}", "tenqPre": pp(Nf["10q"]["pre"]["mean"]), "eightkPre": pp(Nf["8k"]["pre"]["mean"]), "corrEightK": f"{Nf['8k']['corr_with_baseline']:.2f}", "hrTenK": f"{hr0['z_AI_10k']['slope_pct']:+.2f}", "hrTenKT": f"{hr0['z_AI_10k']['t']:.2f}", "hrEightK": f"{hr0['z_AI_8k']['slope_pct']:+.2f}", "hrEightKT": f"{hr0['z_AI_8k']['t']:.2f}", "hrTenQ": f"{hr0['z_AI_10q']['slope_pct']:+.2f}", "hrTenQT": f"{hr0['z_AI_10q']['t']:.2f}", "formWaldP": f"{wd['p']:.2f}", "formWaldPraw": f"{wd0['p']:.2f}", "formwFull": pp(Nf["formw"]["full_w"]["mean"]), "formwFullT": tv(Nf["formw"]["full_w"]["t"]), "cohortTwentyTwoPostShare": f"{c22['share_chunks_post_launch'] * 100:.1f}", "cohortTwentyTwoPreOnly": f1(c22["spread_prelaunch_text_only_w_pct"]), "cohortTwentyTwoFullSame": f1(c22["spread_full_text_same_firms_w_pct"]),
@@ -629,7 +657,7 @@ MAC.update({"nObs": n(R["n_obs"]), "nFirms": n(R["n_firms"]), "nFirmYearsPanel":
629 657 "esgPost": pp(PLm["ESG commitment"]["post"]["mean"]), "climPost": pp(PLm["Climate risk"]["post"]["mean"]), "cyberPost": pp(PLm["Cybersecurity"]["post"]["mean"]), "autoPost": pp(PLm["Automation/labor"]["post"]["mean"]), "cyberPostW": pp(PLm["Cybersecurity"]["post_w"]["mean"]), "cyberIntT": f"{PLm['Cybersecurity']['interaction']['t_firm']:.2f}", "cyberIntTtw": f"{PLm['Cybersecurity']['interaction']['t_twoway']:.2f}", "cyberIntRW": f"{rw['Cybersecurity']:.2f}", "riskIntTtw": f"{PLm['AI disruption risk']['interaction']['t_twoway']:.2f}", "esgIntTtw": f"{PLm['ESG commitment']['interaction']['t_twoway']:.2f}",
630 658 "riskPost": pp(PLm["AI disruption risk"]["post"]["mean"]), "riskPostT": tv(PLm["AI disruption risk"]["post"]["t"]), "riskPre": pp(PLm["AI disruption risk"]["pre"]["mean"]), "adoptPost": pp(PLm["AI adoption"]["post"]["mean"]), "infraPost": pp(PLm["AI infrastructure"]["post"]["mean"]), "macroPost": pp(PLm["Macro risk"]["post"]["mean"]), "riskFFsixPost": f1(R["ls_alpha"]["AI_RISK"]["post"]["ff6_alpha_ann_pct"]), "riskFFsixPostT": tv(R["ls_alpha"]["AI_RISK"]["post"]["ff6_alpha_t"]), "pcaPostW": pp(SP["AI_PC1"]["post_w"]["mean"]), "pcaFullW": pp(SP["AI_PC1"]["full_w"]["mean"]) if "full_w" in SP["AI_PC1"] else pp(S["AI_PC1_EW_w"]["Q5_minus_Q1"]), "pcaFullWT": tv(S["AI_PC1_EW_w"]["t_stat"]), "pcaEVR": f"{R['pca']['evr'][0] * 100:.1f}", "pcaPost": pp(SP["AI_PC1"]["post"]["mean"]),
631 659 "nEntrantFirms": n(EN["n_firms"] - EN["n_seasoned"]), "nSeasoned": n(EN["n_seasoned"]), "rankAuto": f"{cv['rank_autocorr_lag1']:.2f}", "rankAutoFive": f"{cv['rank_autocorr_lag5']:.2f}", "entrantPrem": f1(EN["entrant_minus_seasoned_fwd_pct"]), "seasonedPost": pp(EN["seasoned"]["post"]["mean"]), "seasonedPre": pp(EN["seasoned"]["pre"]["mean"]), "seasonedFullW": pp(EN["seasoned"]["full_w"]["mean"]), "seasonedFullWT": tv(EN["seasoned"]["full_w"]["t"]), "listedPre": pp(EN["listed36"]["pre"]["mean"]), "listedPost": pp(EN["listed36"]["post"]["mean"]), "magSevenPost": pp(M7["ex_mag7_post"]["mean"]), "exTopFiveHundredPost": pp(M7["ex_top500_post"]["mean"]), "exTopFiveHundredPre": pp(M7["ex_top500_pre"]["mean"]), "sixmPost": pp(AH["6m"]["post"]["mean"]), "iwvPost": pp(AB["iwv_adj"]["post"]["mean"]), "iwvPre": pp(AB["iwv_adj"]["pre"]["mean"]), "fvolPost": f1(R2["fwd_vol"]["post"]["mean"] * 100), "fvolPostT": tv(R2["fwd_vol"]["post"]["t"]), "fvolPre": f1(R2["fwd_vol"]["pre"]["mean"] * 100),
632 − "breakInfraT": tv(B["AI_INFRA"]["interaction_t_firm"]), "breakComposT": tv(B["AI_COMPOS"]["interaction_t_firm"]), "breakComposTtw": tv(B["AI_COMPOS"]["interaction_t_twoway"]), "breakComposPwild": pv(B["AI_COMPOS"].get("interaction_p_wild_webb")), "breakCompos": f"{B['AI_COMPOS']['interaction'] * 100:+.2f}", "vwQone": pp(S["AI_COMPOS_VW"]["Q1"]), "vwQfive": pp(S["AI_COMPOS_VW"]["Q5"]), "ewQone": pp(S["AI_COMPOS_EW"]["Q1"]), "ewQfive": pp(S["AI_COMPOS_EW"]["Q5"]), "vwMaxWeight": f"{R['vw_diag']['max_single_firm_weight_v2'] * 100:.0f}", "vwMaxWeightVone": f"{R['vw_diag']['max_single_firm_weight_v1'] * 100:.1f}",
660 + "breakInfraT": tv(B["AI_INFRA"]["interaction_t_firm"]), "breakComposT": tv(B["AI_COMPOS"]["interaction_t_firm"]), "breakComposTtw": tv(B["AI_COMPOS"]["interaction_t_twoway"]), "breakComposPwild": pv(B["AI_COMPOS"].get("interaction_p_wild_webb")), "breakCompos": f"{B['AI_COMPOS']['interaction'] * 100:+.2f}", "vwQone": pp(S["AI_COMPOS_VW"]["Q1"]), "vwQfive": pp(S["AI_COMPOS_VW"]["Q5"]), "ewQone": pp(S["AI_COMPOS_EW"]["Q1"]), "ewQfive": pp(S["AI_COMPOS_EW"]["Q5"]), "vwMaxWeight": f"{R['vw_diag']['max_single_firm_weight_v2'] * 100:.0f}", "vwMaxWeightVone": f"{R['vw_diag']['max_single_firm_weight_v1'] * 100:.1f}", "vwMaxWeightAll": f"{VWALL['max'] * 100:.0f}", "vwMaxWeightAllSort": VWALL["sort"], "vwMaxWeightAllCell": vwcell(VWALL), "vwCellsAboveAll": str(VWALL["n_above"]), "vwCellsAboveAllDistinct": str(VWALL["n_above_distinct"]), "vwSortsAboveAll": " and ".join(VWALL["sorts_above"]), "vwMaxWeightCompos": f"{VWCOMP['max'] * 100:.0f}", "vwMaxWeightComposCell": vwcell(VWCOMP), "vwCellsAboveCompos": str(VWCOMP["n_above"]), "vwMaxWeightComposVone": f"{VWCOMP1['max'] * 100:.1f}", "vwCellsAboveComposVone": str(VWCOMP1["n_above"]),
633 661 "feCompos": f"{PF['M7_all_spid']['coefs']['z_AI_COMPOS']['coef'] * 100:+.2f}", "feComposT": f"{PF['M7_all_spid']['coefs']['z_AI_COMPOS']['t']:.2f}", "feAdopt": f"{PF['M2_adopt_ctrl']['coefs']['z_AI_ADOPT']['coef'] * 100:+.2f}", "feAdoptT": f"{PF['M2_adopt_ctrl']['coefs']['z_AI_ADOPT']['t']:.2f}", "feInfra": f"{PF['M3_infra_ctrl']['coefs']['z_AI_INFRA']['coef'] * 100:+.2f}", "feInfraT": f"{PF['M3_infra_ctrl']['coefs']['z_AI_INFRA']['t']:.2f}", "feSize": f"{PF['M2_adopt_ctrl']['coefs']['z_size']['coef'] * 100:+.2f}", "feSizeT": f"{PF['M2_adopt_ctrl']['coefs']['z_size']['t']:.2f}",
634 662 "nLargeFirms": n(Gm["large_top1000"]["firms"]), "nSmallFirms": n(Gm["small_rest"]["firms"]), "nSmall": n(Gm["small_rest"]["n"]), "largeFFsixPost": f1(Gm["large_top1000"]["ls"]["post"]["ff6_alpha_ann_pct"]), "largeFFsixPostT": tv(Gm["large_top1000"]["ls"]["post"]["ff6_alpha_t"]), "largeSharpePost": f"{Gm['large_top1000']['ls']['post']['sharpe']:.2f}", "smallSharpePost": f"{Gm['small_rest']['ls']['post']['sharpe']:.2f}", "smallSharpePre": f"{Gm['small_rest']['ls']['pre']['sharpe']:.2f}", "largeSharpePre": f"{Gm['large_top1000']['ls']['pre']['sharpe']:.2f}", "smallFull": pp(Gm["small_rest"]["sort_EW"]["Q5_minus_Q1"]), "smallFullT": tv(Gm["small_rest"]["sort_EW"]["t_stat"]), "largeFull": pp(Gm["large_top1000"]["sort_EW"]["Q5_minus_Q1"]), "largeFullT": tv(Gm["large_top1000"]["sort_EW"]["t_stat"]), "smallFullW": pp(Gm["small_rest"]["sort_EW_w"]["Q5_minus_Q1"]), "smallFullWT": tv(Gm["small_rest"]["sort_EW_w"]["t_stat"]), "largeFullW": pp(Gm["large_top1000"]["sort_EW_w"]["Q5_minus_Q1"]), "largeFullWT": tv(Gm["large_top1000"]["sort_EW_w"]["t_stat"]), "sizeGroupChange": f"{MD['share_firm_years_changing_size_group'] * 100:.1f}", "nNoCap": n(META["n_est_without_cap"]), "nNoCapVone": n(META["n_est_without_cap_v1"]),
635 663 "sizeNeuFull": pp(sn["full"]["Q5_minus_Q1"]), "sizeNeuFullT": tv(sn["full"]["t_stat"]), "sizeNeuPost": pp(sn["post"]["mean"]), "sizeNeuPre": pp(sn["pre"]["mean"]), "sizeNeuFullW": pp(sn["full_w"]["Q5_minus_Q1"]), "sizeNeuFullWT": tv(sn["full_w"]["t_stat"]), "largeCohorts": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["large_top1000"]["post"]["by_cohort"].items()), "smallCohorts": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["small_rest"]["post"]["by_cohort"].items()), "largeCohortsW": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["large_top1000"]["post_w"]["by_cohort"].items()), "smallCohortsW": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["small_rest"]["post_w"]["by_cohort"].items()),
modified scripts/stageE_writing_macros.py +14 −0
@@ -37,6 +37,17 @@ def num(x: float, d: int = 2) -> str:
37 37 return f"{x:.{d}f}"
38 38
39 39
40 +def ordinal(x) -> str:
41 + """'4' -> '4th', '21' -> '21st', '63' -> '63rd', '11' -> '11th'; a non-integer value (e.g. a percentile '99.8') takes 'th'.
42 + Stage F (NB-4): the suffix is generated with the rank so the two cannot diverge on regeneration."""
43 + sx = str(x)
44 + if not sx.lstrip("-").isdigit():
45 + return f"{sx}th"
46 + k = int(sx)
47 + sfx = "th" if 10 <= k % 100 <= 20 else {1: "st", 2: "nd", 3: "rd"}.get(k % 10, "th")
48 + return f"{k}{sfx}"
49 +
50 +
40 51 def thousands(n: int) -> str:
41 52 return f"{int(n):,}".replace(",", "{,}")
42 53
@@ -121,6 +132,9 @@ M["mIdaRankCore"] = str(int(ranks["IDA"]["rc"]))
121 132 M["mHeRank"] = str(int(ranks["HE"]["r"]))
122 133 M["mHeRankStrip"] = str(int(ranks["HE"]["rs"]))
123 134 M["mHeRankCore"] = str(int(ranks["HE"]["rc"]))
135 +for _k in ("mIdaRank", "mIdaRankStrip", "mIdaRankCore", "mHeRank", "mHeRankStrip", "mHeRankCore", "mUtilSectorRank",
136 + "mProbeNegatedPct", "mProbeUtilityPct", "mProbeParaphrasePct", "mProbeCyberPct", "mProbeUnrelatedPct"):
137 + M[_k + "Ord"] = ordinal(M[_k]) # NB-4: ordinal forms emitted with the values ("4th", "2nd", "99.8th")
124 138 M["mTopThirtyUtilOriginal"] = str(alt["top30_utilities_original"])
125 139 M["mTopThirtyUtilCore"] = str(alt["top30_utilities_core_strip"])
126 140 M["mTopThirtyOverlapStrip"] = str(alt["top30_overlap_strip"])
127 141
Menu

Theme