SPB Git
4.8 KB · 108 lines python
1"""Determinants of announced project capital.23DV: log(total_amount_usd) for the 9,321 projects with a stated dollar figure.4Hedonic-style regressions with project, status, sector, year and firm fixed5effects; cluster-robust SE by firm.6"""7from __future__ import annotations89import warnings10import numpy as np11import pandas as pd12import statsmodels.formula.api as smf1314from common import DATA, set_style, savefig, write_table, save_results15import matplotlib.pyplot as plt1617warnings.filterwarnings("ignore")18set_style()1920p = pd.read_parquet(DATA / "projects.parquet")21d = p[p["has_amount"]].copy()22d["ann_year"] = d["ann_year"].astype(int)23d["sector"] = d["sector"].fillna("Unknown")24d["log_nfil"] = np.log(d["n_filings"].clip(lower=1))25d["is_ai_i"] = d["is_ai"].astype(int)26# baseline type = the modal type so coefficients read as deviations27base_type = d["project_type"].mode().iloc[0]28d["project_type"] = pd.Categorical(d["project_type"],29                                   categories=[base_type] + [t for t in d["project_type"].unique() if t != base_type])30groups = d["cik"]3132# Model 1: sector + year FE33m1 = smf.ols("log_amount ~ is_ai_i + n_tech + log_nfil + C(status) + C(project_type) + C(sector) + C(ann_year)",34             data=d).fit(cov_type="cluster", cov_kwds={"groups": groups})35# Model 2: firm + year FE (absorb 500 firm dummies)36m2 = smf.ols("log_amount ~ is_ai_i + n_tech + log_nfil + C(status) + C(project_type) + C(cik) + C(ann_year)",37             data=d).fit(cov_type="cluster", cov_kwds={"groups": groups})3839KEEP = ["is_ai_i", "n_tech", "log_nfil",40        "C(status)[T.in_progress]", "C(status)[T.completed]", "C(status)[T.planned]"]41LBL = {"is_ai_i": "AI project", "n_tech": "\\# technologies", "log_nfil": "log(\\# filings)",42       "C(status)[T.in_progress]": "Status: in progress", "C(status)[T.completed]": "Status: completed",43       "C(status)[T.planned]": "Status: planned"}444546def fmt(m, k):47    if k not in m.params.index:48        return None49    b, se, pv = m.params[k], m.bse[k], m.pvalues[k]50    star = "***" if pv < .01 else "**" if pv < .05 else "*" if pv < .1 else ""51    return f"{b:.3f}{star}", f"({se:.3f})"525354lt = [r"\begin{tabular}{lcc}", r"\toprule",55      r" & (1) Sector+Year FE & (2) Firm+Year FE \\", r"\midrule"]56for k in KEEP:57    a, b = fmt(m1, k), fmt(m2, k)58    lt.append(f"{LBL[k]} & {a[0] if a else ''} & {b[0] if b else ''} \\\\")59    lt.append(f" & {a[1] if a else ''} & {b[1] if b else ''} \\\\")60lt += [r"\midrule",61       r"Project-type FE & Yes & Yes \\",62       r"Sector FE & Yes & -- \\",63       r"Firm FE & -- & Yes \\",64       r"Year FE & Yes & Yes \\",65       f"Observations & {int(m1.nobs):,} & {int(m2.nobs):,} \\\\",66       f"$R^2$ & {m1.rsquared:.3f} & {m2.rsquared:.3f} \\\\",67       r"\bottomrule", r"\end{tabular}"]68write_table("tab_amount_models", "\n".join(lt))6970# ---- coefficient plot: project-type premia (model 2) ----------------------71TYPE_LABEL = {72    "ma_integration": "M&A integration", "capex_program": "Capex program",73    "plant_construction": "Plant construction", "infrastructure": "Infrastructure",74    "data_center": "Data center", "energy_transition": "Energy transition",75    "product_launch": "Product launch", "rd_program": "R&D program",76    "ai_initiative": "AI initiative", "sustainability": "Sustainability",77    "cost_reduction": "Cost reduction", "geographic_expansion": "Geo. expansion",78    "manufacturing_expansion": "Mfg. expansion", "partnership": "Partnership",79    "supply_chain": "Supply chain", "automation": "Automation",80    "cloud_migration": "Cloud migration", "digital_transformation": "Digital transf.",81    "erp_implementation": "ERP", "technology_deployment": "Tech. deployment",82    "drug_pipeline": "Drug pipeline",83}84rows = []85for k in m2.params.index:86    if k.startswith("C(project_type)[T."):87        t = k.split("[T.")[1].rstrip("]")88        rows.append((TYPE_LABEL.get(t, t), m2.params[k], m2.bse[k]))89rows.sort(key=lambda r: r[1])90fig, ax = plt.subplots(figsize=(6.8, 6.2))91ys = range(len(rows))92ax.errorbar([r[1] for r in rows], list(ys),93            xerr=[1.96 * r[2] for r in rows], fmt="o", color="#1b3a5b", capsize=3, ms=4)94ax.axvline(0, color="grey", ls="--", lw=0.8)95ax.set_yticks(list(ys)); ax.set_yticklabels([r[0] for r in rows], fontsize=8)96ax.set_xlabel(f"log-capital premium vs. {TYPE_LABEL.get(base_type, base_type)} (firm+year FE)")97ax.set_title("Announced-capital premia by project type")98savefig(fig, "fig_amount_coef.pdf")99100save_results("amount_models", {101    "n": int(m1.nobs), "baseline_type": base_type,102    "r2_sector": float(m1.rsquared), "r2_firm": float(m2.rsquared),103    "ai_coef": [float(m2.params["is_ai_i"]), float(m2.bse["is_ai_i"]), float(m2.pvalues["is_ai_i"])],104    "ntech_coef": [float(m2.params["n_tech"]), float(m2.pvalues["n_tech"])],105    "ai_pct_effect": float(np.exp(m2.params["is_ai_i"]) - 1),106})107print("amount models done | R2(firm FE) =", round(m2.rsquared, 3))108
Files · df7905dcfb953e1cfed244542eaddc2026d51df6
Menu

Theme