"""05_make_tables.py -- emit LaTeX table fragments for the report.""" import json, pandas as pd, numpy as np T="results/tables"; R="paper/tables" RES=json.load(open(f"{T}/results.json")) def w(name, body): open(f"{R}/{name}.tex","w").write(body) # --- Table 1: summary stats --- s=pd.read_csv(f"{T}/t1_summary_stats.csv",index_col=0) nice={"ai_adoption_wmean":"AI adoption (wmean)","ai_infrastructure_wmean":"AI infrastructure (wmean)", "ai_disruption_risk_wmean":"AI disruption risk (wmean)","fwd_ret":"Forward 12m return", "fwd_ret_mktadj":"Forward return, mkt-adj.","mom_12m":"Momentum (12m)","vol_12m":"Volatility (ann.)", "log_mktcap":"Log market cap"} rows="".join(f"{nice.get(i,i)} & {s.loc[i,'mean']:.3f} & {s.loc[i,'std']:.3f} & {s.loc[i,'5%']:.3f} & {s.loc[i,'50%']:.3f} & {s.loc[i,'95%']:.3f} \\\\\n" for i in nice) w("tab_summary", r"""\begin{table}[H]\centering\small \caption{Summary statistics, firm-year panel with tradeable forward returns (N=%d firm-years, %d firms, formation years %s).}\label{tab:summ} \begin{tabular}{lrrrrr}\toprule Variable & Mean & SD & p5 & Median & p95 \\\midrule %s\bottomrule \end{tabular}\end{table}""" % (RES["n_obs"],RES["n_firms"],RES["yrs"],rows)) # --- Table 2: portfolio sorts --- sd=pd.read_csv(f"{T}/t2_portfolio_sorts.csv",index_col=0) def row(key,label): r=sd.loc[key] return f"{label} & {r.Q1*100:.1f} & {r.Q2*100:.1f} & {r.Q3*100:.1f} & {r.Q4*100:.1f} & {r.Q5*100:.1f} & {r.Q5_minus_Q1*100:+.1f} & {r.t_stat:.2f} \\\\\n" body =row("AI_ADOPT_EW","AI adoption (EW)") body+=row("AI_ADOPT_VW","\\quad value-weighted") body+=row("AI_INFRA_EW","AI infrastructure (EW)") body+=row("AI_INFRA_VW","\\quad value-weighted") body+=row("AI_RISK_EW","AI disruption risk (EW)") body+=row("AI_RISK_VW","\\quad value-weighted") body+="\\midrule\n"+row("AI_COMPOS_EW","AI composite (EW)") body+=row("AI_COMPOS_VW","\\quad value-weighted") w("tab_sorts", r"""\begin{table}[H]\centering\small \caption{Forward 12-month returns (\%%) by AI-disclosure quintile. Q5$-$Q1 is the high-minus-low spread; \textit{t} uses Newey--West (3 lag) errors on the 15 annual spreads.}\label{tab:sorts} \begin{tabular}{lrrrrrrr}\toprule Signal (weighting) & Q1 & Q2 & Q3 & Q4 & Q5 & Q5$-$Q1 & \textit{t} \\\midrule %s\bottomrule \end{tabular}\end{table}""" % body) # --- Table 3: factor alphas --- a=RES["ls_alpha"] def arow(k,label): x=a[k] return (f"{label} & {x['mean_ls_ann']*100:+.1f} & {x['capm_alpha_m']*1200:+.1f} & {x['capm_alpha_t']:.2f} " f"& {x['ff6_alpha_ann']*100:+.1f} & {x['ff6_alpha_t']:.2f} \\\\\n") body=arow("AI_ADOPT","AI adoption")+arow("AI_INFRA","AI infrastructure")+arow("AI_COMPOS","AI composite")+arow("AI_RISK","AI disruption risk") w("tab_alpha", r"""\begin{table}[H]\centering\small \caption{Risk-adjusted performance of the equal-weight high-minus-low (Q5$-$Q1) long-short portfolio, annually rebalanced (%d monthly observations). Alphas annualized; \textit{t} from Newey--West (3 lag) HAC errors.}\label{tab:alpha} \begin{tabular}{lrrrrr}\toprule & Mean LS & CAPM $\alpha$ & \textit{t} & FF6 $\alpha$ & \textit{t} \\ Signal & (\%%/yr) & (\%%/yr) & & (\%%/yr) & \\\midrule %s\bottomrule \end{tabular}\end{table}""" % (a["AI_ADOPT"]["n_months"], body)) # --- Table 4: panel FE + Fama-MacBeth --- fe=RES["panel_fe"]; fm=RES["fama_macbeth"] def g(d,v): return d["coefs"].get(v) m5=fe["M5_all"]; fmall=fm["all"] def cell(coef,t): return f"{coef:+.4f} \\tstat{{{t:.2f}}}" rows=[] labels=[("z_AI_ADOPT","AI adoption ($z$)"),("z_AI_INFRA","AI infrastructure ($z$)"), ("z_AI_RISK","AI disruption risk ($z$)"),("z_size","Size ($z$)"), ("z_mom","Momentum ($z$)"),("z_vol","Volatility ($z$)")] for v,lab in labels: fec=m5["coefs"].get(v); fmc=fmall.get(v) fecell = cell(fec["coef"],fec["t"]) if fec else "---" fmcell = cell(fmc["mean_coef"],fmc["t"]) if fmc else "---" rows.append(f"{lab} & {fecell} & {fmcell} \\\\") body="\n".join(rows) w("tab_panel", r"""\begin{table}[H]\centering\small \caption{Forward-return regressions. Column (1): two-way (firm $\times$ year) fixed-effects panel, SE double-clustered by firm and year. Column (2): Fama--MacBeth, Newey--West \textit{t}. Dependent variable is the winsorized forward 12-month return; signals are within-year $z$-scores.}\label{tab:panel} \begin{tabular}{lcc}\toprule & (1) Two-way FE & (2) Fama--MacBeth \\\midrule %s \midrule Firm FE & Yes & No \\ Year FE & Yes & (by construction) \\ N (obs / years) & %d & %d \\ \bottomrule \end{tabular}\end{table}""" % (body, fe["M5_all"]["n"], fmall["n_years"])) # --- Table 5: structural break --- br=RES["structural_break"]; sp=RES["subperiod_spread"] def brow(k,label): b=br[k]; s=sp[k] pre=s["pre_2010_2021"]; post=s["post_2022_2024"] return (f"{label} & {pre[0]*100:+.1f} \\tstat{{{pre[1]:.2f}}} & {post[0]*100:+.1f} \\tstat{{{post[1]:.2f}}} " f"& {b['interaction']:+.4f} \\tstat{{{b['interaction_t']:.2f}}} \\\\\n") body=brow("AI_ADOPT","AI adoption")+brow("AI_INFRA","AI infrastructure")+brow("AI_COMPOS","AI composite") w("tab_break", r"""\begin{table}[H]\centering\small \caption{The post-ChatGPT structural break. Columns 2--3: Q5$-$Q1 forward-return spread (\%%/yr, Newey--West \textit{t}) in each sub-period. Column 4: coefficient on AI~$z$\,$\times$\,Post-2022 from a pooled OLS (size and momentum controls, year-clustered SE).}\label{tab:break} \begin{tabular}{lccc}\toprule & \multicolumn{2}{c}{Q5$-$Q1 spread} & Interaction \\ \cmidrule(lr){2-3} Signal & Pre (2010--21) & Post (2022--24) & AI$\times$Post \\\midrule %s\bottomrule \end{tabular}\end{table}""" % body) print("LaTeX table fragments written.")