#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Régénère paper/tables/regression.tex à partir de revision_extras.json avec SE clusterisées par municipalité (et stars associées).""" import json R = json.load(open("results/revision_extras.json")) def star(p): return "^{***}" if p < .01 else "^{**}" if p < .05 else "^{*}" if p < .1 else "" def cell(model, k): c = R[model]["coef"].get(k) if not c: return "", "" b, se, p = c["b"], c["se_cl"], c["p_cl"] s = "$-$" if b < 0 else "" st = star(p) coef = f"{s}{abs(b):.3f}" + (f"${st}$" if st else "") return coef, f"({se:.3f})" disp = [("ln_sqft", r"$\ln(\text{Living area})$"), ("ln_lot", r"$\ln(\text{Lot area})$"), ("beds", "Bedrooms"), ("baths", "Bathrooms"), ("parking", "Parking"), ("age", "Age"), ("__A__", r"\textit{Amenities \& text (LLM)}"), ("central_air", "Central A/C"), ("transit", "Public transit"), ("waterfront", "Waterfront"), ("g_cond", "Condition (LLM, ord.)"), ("g_renovated_kitchen", r"Reno.\ kitchen (LLM)"), ("g_hardwood", "Hardwood floor (LLM)"), ("g_new_construction", "New construction (LLM)"), ("g_income_property", "Income property (LLM)"), ("__V__", r"\textit{Vision (façade)}"), ("v_curb", "Curb appeal (1--10)"), ("v_reno", "Renovation level (ord.)"), ("v_luxury", "Luxury (visual)"), ("v_garage_vis", "Garage visible"), ("v_mat_brique", "Brick façade"), ("v_mat_pierre", "Stone façade"), ("v_mat_vinyle", "Vinyl façade"), ("v_set_rural", "Rural setting")] L = [] for k, lab in disp: if k in ("__A__", "__V__"): L.append(f" \\multicolumn{{3}}{{@{{}}l}}{{{lab}}} \\\\[2pt]") continue a1, a2 = cell("modelA", k) b1, b2 = cell("modelB", k) L.append(f" {lab} & {a1} & {b1} \\\\") L.append(f" & {a2} & {b2} \\\\[3pt]") nA, nB = R["modelA"]["n"], R["modelB"]["n"] tex = r"""% Auto-generated (revision: clustered SEs) \begin{table}[htbp] \centering \begin{threeparttable} \caption{Hedonic Regression Results (Text + Vision Features)} \label{tab:regression} \begin{tabular}{@{}lcc@{}} \toprule & (1) Model~A & (2) Model~B \\ & \textit{rich} & \textit{broad} \\ \midrule """ + "\n".join(L) + f""" \\midrule City FE / Type FE & Yes & Yes \\\\ Living area & Yes & No \\\\ Observations & {nA:,} & {nB:,} \\\\ $R^2$ & {R['modelA']['r2']:.3f} & {R['modelB']['r2']:.3f} \\\\ Adjusted $R^2$ & {R['modelA']['adj']:.3f} & {R['modelB']['adj']:.3f} \\\\ \\bottomrule \\end{{tabular}} """.replace(f"{nA:,}", f"{nA:,}".replace(",", "{,}")).replace(f"{nB:,}", f"{nB:,}".replace(",", "{,}")) + r""" \begin{tablenotes}[flushleft] \footnotesize \item \textit{Notes:} OLS estimates of Equation~\eqref{eq:hedonic} with the full feature set. Model~A includes $\ln(\text{Living area})$ (measured-area subsample); Model~B omits it (full sample). ``LLM'' variables are extracted from the description text; ``Vision'' variables from the façade photograph. Curb appeal and renovation level enter as scores; luxury, garage, and material/setting are indicators. All specifications include city and property-type fixed effects and the full amenity, room-detail, and listing-signal controls (only selected coefficients shown). Standard errors clustered by municipality (195 clusters) in parentheses. Heteroskedasticity-robust (HC3) standard errors are uniformly smaller and yield the same significance pattern for the text- and vision-derived attributes, except that curb appeal in Model~B is significant under HC3 but not under clustering. $^{***}$, $^{**}$, $^{*}$: significance at 1\%, 5\%, 10\%. \end{tablenotes} \end{threeparttable} \end{table} """ open("paper/tables/regression.tex", "w").write(tex) print("-> paper/tables/regression.tex (clustered SEs)")