SPB Git

spb/wp5_uqo Public

UQO Working Paper No. 5 — Airbnb, residential rents and housing market pressure.

TeX 53.4% Python 46.5%
3.9 KB · 130 lines python
Raw Blame History
1# Author: Simon-Pierre Boucher — contact@spboucher.ai2"""3Shared LaTeX table helpers for the regression scripts.45`significance_star` and `results_to_latex` reproduce byte-for-byte the table6format of the published tables (stargazer-style booktabs tables with HC17robust standard errors in parentheses).89Tables are written as *fragments* (no ``\\begin{table}`` wrapper): the paper10wraps each ``\\input`` in its own ``table`` environment with caption and label.11"""1213from pathlib import Path141516def significance_star(pval: float) -> str:17    """Return conventional significance stars for a p-value."""18    if pval < 0.01:19        return "***"20    elif pval < 0.05:21        return "**"22    elif pval < 0.10:23        return "*"24    return ""252627def results_to_latex(28    results_list: list,29    model_names: list[str],30    dep_var: str,31    display_vars: list[str] | None = None,32    out_path: Path | None = None,33    note: str = "",34) -> str:35    """36    Produce a stargazer-style LaTeX table from a list of OLS results.3738    Parameters39    ----------40    results_list : list of statsmodels RegressionResultsWrapper41    model_names  : column headers for each model42    dep_var      : dependent variable label for the table caption row43    display_vars : subset of variable names to display (None = all)44    out_path     : file to write; None = no write45    note         : optional footnote text46    """47    n_models = len(results_list)4849    # Collect the union of variable names across all models50    if display_vars is None:51        all_vars: list[str] = []52        for res in results_list:53            for v in res.params.index:54                if v not in all_vars:55                    all_vars.append(v)56        display_vars = all_vars5758    # Build LaTeX59    col_spec = "l" + "c" * n_models60    lines: list[str] = []61    lines.append(r"\small")62    lines.append(r"\begin{tabular}{" + col_spec + "}")63    lines.append(r"\toprule")6465    # Header66    header = " & ".join([""] + [f"\\textbf{{{mn}}}" for mn in model_names]) + r" \\"67    lines.append(header)68    lines.append(69        " & ".join(["Dep.\\ var:"] + [f"\\textit{{{dep_var}}}" for _ in model_names])70        + r" \\"71    )72    lines.append(r"\midrule")7374    # Coefficients75    for var in display_vars:76        coef_cells = []77        se_cells = []78        for res in results_list:79            if var in res.params.index:80                b = res.params[var]81                se = res.bse[var]82                p = res.pvalues[var]83                star = significance_star(p)84                coef_cells.append(f"{b:.4f}{star}")85                se_cells.append(f"({se:.4f})")86            else:87                coef_cells.append("")88                se_cells.append("")89        # Pretty variable name90        vname = var.replace("_", r"\_")91        lines.append(" & ".join([vname] + coef_cells) + r" \\")92        lines.append(" & ".join([""] + se_cells) + r" \\[4pt]")9394    lines.append(r"\midrule")9596    # Fit statistics97    for label, accessor in [98        ("Observations", lambda r: f"{int(r.nobs)}"),99        ("R$^2$", lambda r: f"{r.rsquared:.4f}"),100        ("Adj.\\ R$^2$", lambda r: f"{r.rsquared_adj:.4f}"),101    ]:102        cells = [label]103        for res in results_list:104            try:105                cells.append(accessor(res))106            except Exception:107                cells.append("")108        lines.append(" & ".join(cells) + r" \\")109110    lines.append(r"\bottomrule")111    lines.append(r"\end{tabular}")112113    # Note114    if note:115        lines.append(r"\vspace{4pt}")116        lines.append(r"\parbox{\textwidth}{\footnotesize " + note + "}")117118    lines.append(119        r"\parbox{\textwidth}{\footnotesize Robust (HC1) standard errors in "120        r"parentheses. $^{***}p<0.01$; $^{**}p<0.05$; $^{*}p<0.10$.}"121    )122123    tex = "\n".join(lines) + "\n"124125    if out_path is not None:126        out_path.write_text(tex, encoding="utf-8")127        print(f"  -> saved {out_path}")128129    return tex130