spb/wp3_uqo Public
UQO Working Paper No. 3 — Hedonic housing price models for the US: parametric, quantile, and machine-learning approaches.
TeX 77.8%
Python 22.1%
1# Author: Simon-Pierre Boucher — contact@spboucher.ai2#3"""Publication style shared by all paper figures.45Design goals (housing/urban economics journal standard):6 - serif typography consistent with the paper's newtx text font;7 - muted, colorblind-safe palette (no default-Stata / default-matplotlib look);8 - no chart junk: top/right spines removed, light dotted y-grid only;9 - human-readable variable labels instead of raw column names;10 - panel titles "(a) ..." left-aligned, no redundant figure-level titles11 (the LaTeX caption carries the title).12"""1314import matplotlib.pyplot as plt1516# Muted palette17PRIMARY = "#3B6B9B" # deep steel blue (marks, bars, boxes)18PRIMARY_LIGHT = "#7FA5C6" # scatter clouds19ACCENT = "#A63E38" # muted brick red (reference lines, binned means)20NEUTRAL = "#6E6E6E" # gray for zero lines / secondary elements21CMAP_SEQ = "Blues" # densities (hexbin)22CMAP_DIV = "coolwarm" # SHAP dependence coloring23DPI = 3002425# Human-readable labels for model variables (used in axis labels and SHAP plots)26LABELS = {27 "ln_sqft": "Log(Living Area)",28 "ln_lot": "Log(Lot Size)",29 "ln_hoa": "Log(HOA Fee)",30 "ln_price": "Log(Price)",31 "bedrooms": "Bedrooms",32 "bathrooms": "Bathrooms",33 "age": "Property Age",34 "age_sq": "Property Age$^2$",35 "stories": "Stories",36 "bath_per_bed": "Baths per Bedroom",37 "sqft_per_bed": "Sqft per Bedroom",38 "parking_spaces": "Parking Spaces",39 "luxury_score": "Luxury Score",40 "walk_score": "Walk Score",41 "bike_score": "Bike Score",42 "transit_score": "Transit Score",43 "school_count": "School Count",44 "avg_school_rating": "Avg. School Rating",45 "nearest_school_distance": "Nearest School Dist.",46 "property_tax_rate": "Property Tax Rate",47 "has_pool": "Pool",48 "has_spa": "Spa",49 "has_basement": "Basement",50 "has_fireplace": "Fireplace",51 "has_garage": "Garage",52 "on_waterfront": "Waterfront",53 "has_central_air": "Central Air",54 "has_forced_air": "Forced Air",55 "has_hardwood": "Hardwood Floors",56 "is_condo": "Condo",57 "has_hoa": "HOA",58 "tag_new_construction": "New Construction",59 "tag_foreclosure": "Foreclosure",60 "region_Northeast": "Region: Northeast",61 "region_South": "Region: South",62 "region_West": "Region: West",63 "region_Midwest": "Region: Midwest",64 "sqft_x_age": "Sqft $\\times$ Age",65 "pool_x_south": "Pool $\\times$ South",66 "waterfront_x_sqft": "Waterfront $\\times$ Log(sqft)",67 "basement_x_north": "Basement $\\times$ North",68 "condo_x_walkscore": "Condo $\\times$ Walk Score",69 "age_x_luxury": "Age $\\times$ Luxury",70}717273def label(var: str) -> str:74 """Human-readable label for a model variable (falls back to the raw name)."""75 return LABELS.get(var, var)767778def apply_paper_style() -> None:79 """Set the rcParams used for every figure in the paper."""80 plt.rcParams.update({81 "font.family": "serif",82 "mathtext.fontset": "dejavuserif",83 "font.size": 13,84 "axes.titlesize": 14,85 "axes.labelsize": 13.5,86 "legend.fontsize": 12,87 "xtick.labelsize": 12,88 "ytick.labelsize": 12,89 # Clean journal look90 "axes.spines.top": False,91 "axes.spines.right": False,92 "axes.linewidth": 0.9,93 "axes.edgecolor": "#333333",94 "xtick.direction": "out",95 "ytick.direction": "out",96 "axes.grid": True,97 "axes.grid.axis": "y",98 "grid.linestyle": ":",99 "grid.linewidth": 0.7,100 "grid.alpha": 0.45,101 "grid.color": "#999999",102 "legend.frameon": False,103 "figure.dpi": 100,104 "savefig.dpi": DPI,105 "savefig.bbox": "tight",106 "savefig.facecolor": "white",107 })108109110def panel_title(ax, text: str) -> None:111 """Left-aligned panel title, e.g. '(a) OLS'."""112 ax.set_title(text, loc="left", fontweight="regular", pad=8)113