# 🏠 Decoding Real Estate Descriptions ### Semantic Embeddings and Hedonic Pricing of Residential Properties in Quebec **UQO Working Paper No. 2** [![Author](https://img.shields.io/badge/Author-Simon--Pierre%20Boucher-0033A0?style=flat-square)](mailto:contact@spboucher.ai) [![Contact](https://img.shields.io/badge/Contact-contact%40spboucher.ai-D14836?style=flat-square&logo=gmail&logoColor=white)](mailto:contact@spboucher.ai) [![Institution](https://img.shields.io/badge/UQO-D%C3%A9partement%20des%20sciences%20administratives-00539F?style=flat-square)](https://uqo.ca) [![Python](https://img.shields.io/badge/Python-3.14-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/) [![LaTeX](https://img.shields.io/badge/LaTeX-latexmk%20%7C%20pdfLaTeX-008080?style=flat-square&logo=latex&logoColor=white)](https://www.latex-project.org/) [![sentence-transformers](https://img.shields.io/badge/sentence--transformers-all--MiniLM--L6--v2-FF6F00?style=flat-square&logo=huggingface&logoColor=white)](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) [![statsmodels](https://img.shields.io/badge/statsmodels-OLS%20%7C%20QuantReg%20%7C%20HC3-4051B5?style=flat-square)](https://www.statsmodels.org/) [![scikit-learn](https://img.shields.io/badge/scikit--learn-PCA%20%7C%20Lasso%20%7C%20ElasticNet-F7931E?style=flat-square&logo=scikitlearn&logoColor=white)](https://scikit-learn.org/) [![Reproducible](https://img.shields.io/badge/Reproducible-end--to--end%20pipeline-2E7D32?style=flat-square&logo=githubactions&logoColor=white)](#-reproducing-everything) [![Sample](https://img.shields.io/badge/n-17%2C087%20houses-1565C0?style=flat-square)](#-data) [![Model](https://img.shields.io/badge/Adj.%20R%C2%B2-0.452%20%E2%86%92%200.511-6A1B9A?style=flat-square)](#-key-results) [![Paper](https://img.shields.io/badge/Paper-53%20pages%20PDF-B71C1C?style=flat-square&logo=adobeacrobatreader&logoColor=white)](paper/main.pdf) [![References](https://img.shields.io/badge/References-86%20verified%20(OpenAlex%20%2B%20DOI)-00695C?style=flat-square&logo=googlescholar&logoColor=white)](paper/references.bib) [![JEL](https://img.shields.io/badge/JEL-R31%20%C2%B7%20C45%20%C2%B7%20C21%20%C2%B7%20R21-455A64?style=flat-square)](#-citation)
--- > **TL;DR** β€” Listing descriptions written by real estate agents contain price-relevant information that classic hedonic variables miss. This project embeds the free-text `PublicRemarks` of **17,087 Quebec single-family house listings** with a sentence transformer, projects each embedding onto **20 researcher-defined semantic reference descriptions** (*Luxury*, *Needs Renovation*, *Waterfront*, *Motivated Seller*, …) via cosine similarity, and adds those 20 interpretable scores to a log-price hedonic OLS model. Adjusted RΒ² rises from **0.452 to 0.511** (joint *F* = 99.53, *p* < 0.001), and each semantic dimension carries a named, signed, economically meaningful implicit price. --- ## πŸ“– Table of Contents - [Overview](#-overview) - [Key Results](#-key-results) - [Methodology](#-methodology) - [Repository Structure](#-repository-structure) - [The Pipeline, Script by Script](#-the-pipeline-script-by-script) - [Data](#-data) - [Reproducing Everything](#-reproducing-everything) - [Building the Paper](#-building-the-paper) - [Verification & Provenance](#-verification--provenance) - [Figures Gallery](#-figures-gallery) - [Requirements](#-requirements) - [Citation](#-citation) - [Author & Contact](#-author--contact) --- ## πŸ” Overview Hedonic pricing models decompose a property's price into the implicit prices of its characteristics β€” bedrooms, bathrooms, lot size. But the *narrative* of a listing ("cuisine gastronomique, comptoirs de quartz…" vs. "besoin de rΓ©novation, vendu tel quel…") carries quality information that no structured field captures. The catch, historically, is a **depth-vs-interpretability trade-off**: | Approach | Semantic depth | Economic interpretability | |---|---|---| | Keyword counts / bag-of-words | ❌ shallow | βœ… high | | Sentiment scores | ❌ one dimension | βœ… high | | LDA topics | 🟑 moderate | ❌ unstable | | Raw BERT/transformer embeddings | βœ… deep | ❌ opaque (384–768 anonymous dims) | | **Reference-based cosine projection (this paper)** | βœ… **deep** | βœ… **high** | **The idea:** instead of feeding 384 anonymous embedding dimensions into a regression, define 20 short *reference descriptions* β€” synthetic French paragraphs, each embodying one qualitative housing dimension β€” embed them with the same model, and use the **cosine similarity between each listing and each reference** as 20 named regressors. Every coefficient then reads directly as "the implicit price of sounding more *luxury* / more *fixer-upper* / more *waterfront*", per standard deviation. This is conceptually analogous to factor-mimicking portfolios in asset pricing, and to concept-bottleneck models in interpretable ML: the deep representation is channeled through human-named concepts before inference. --- ## πŸ“Š Key Results ### Model comparison (n = 17,087) | Model | Specification | RΒ² | Adj. RΒ² | AIC | BIC | k | |:---:|---|---:|---:|---:|---:|---:| | A | Structural only | 0.4525 | 0.4523 | 26,165 | 26,219 | 6 | | B | A + description length | 0.4647 | 0.4645 | 25,779 | 25,841 | 7 | | C | A + 20 semantic similarities | 0.4962 | 0.4954 | 24,783 | 24,992 | 26 | | **D** | **Full (B + C)** | **0.5122** | **0.5115** | **24,232** | 24,448 | 27 | | E | Parsimonious (16 significant dims) | 0.5118 | 0.5111 | 24,239 | **24,425** | 23 | Joint significance of the 21 text variables (D vs. A): ***F* = 99.53, *p* < 0.001**. Variance decomposition of Model D: structural 88.3 %, text length 2.4 %, semantics 9.3 %. ### Implicit semantic price gradients (Model D, per +1 SD, HC3 errors) | πŸ“ˆ Positive | Impact | πŸ“‰ Negative | Impact | |---|---:|---|---:| | Modern/Contemporary | **+16.4 %*** | Family-Friendly | **βˆ’11.6 %*** | | Luxury | **+14.2 %*** | New Construction | **βˆ’10.5 %*** | | Land & Nature | **+13.0 %*** | Quiet & Peaceful | βˆ’9.5 %*** | | Entry-Level | +10.4 %*** | Bright & Spacious | βˆ’8.9 %*** | | Waterfront | +4.7 %*** | Motivated Seller | **βˆ’8.5 %*** | | Panoramic View | +3.0 %** | Needs Renovation | **βˆ’7.9 %*** | *** p < 0.001, ** p < 0.01. Negative coefficients on Family-Friendly / New Construction largely proxy suburban location (see paper Β§6). Impact = (e^Ξ² βˆ’ 1) Γ— 100 %. ### Heterogeneity & robustness (highlights) - **Quantile regressions** (Ο„ = 0.25 / 0.50 / 0.75): the Luxury premium rises monotonically from **9.7 %** at the 25th percentile to **17.3 %** at the 75th; urgency and condition discounts attenuate at the top of the market. - **Bootstrap** (1,000 replications, seed 42): SEs within 9 % of HC3 (15/20 within 5 %). - **Outlier trimming** (1st/99th price percentiles): no sign reversal, all 16 significant dimensions stay significant. - **Multicollinearity is real and disclosed**: similarity dimensions correlate at 0.66–0.97 (mean VIF 17.2). Block-level inference is unaffected; individual coefficients are partial associations within a correlated block. - **PCA benchmark**: 20 principal components of the raw embeddings fit *better* (Ξ”RΒ² +0.070 vs. +0.044) but are economically unreadable β€” the paper quantifies the interpretability-vs-fit trade-off explicitly. --- ## πŸ§ͺ Methodology ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Property β”‚ β”‚ Sentence β”‚ β”‚ Cosine β”‚ β”‚ Hedonic β”‚ β”‚ Implicit β”‚ β”‚ Listings β”œβ”€β”€β–Άβ”‚ Embeddings β”œβ”€β”€β–Άβ”‚ Similarity β”œβ”€β”€β–Άβ”‚ OLS Model β”œβ”€β”€β–Άβ”‚ Price β”‚ β”‚ (n=17,087) β”‚ β”‚ (384-d) β”‚ β”‚ (20 refs) β”‚ β”‚ (HC3 s.e.) β”‚ β”‚ Estimates β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ PublicRemarks all-MiniLM-L6-v2 S = E Β· Rα΅€ log(P) = Ξ± + Ξ²'X + Ξ΄β„“ + Ξ³'s + Ξ΅ ``` 1. **Extraction** β€” houses only, price > 0, description β‰₯ 20 characters; minimal preprocessing (the transformer handles raw text). 2. **Embedding** β€” `all-MiniLM-L6-v2` (22.7M params, 384-d, L2-normalized), batch 256; the whole corpus encodes in seconds on Apple Silicon. 3. **Projection** β€” 20 French reference descriptions (146–257 chars each, full verbatim text in paper Appendix A), designed for *semantic saturation*, *dimensional specificity*, *linguistic consistency*. One matrix product yields the 17,087 Γ— 20 similarity matrix. 4. **Estimation** β€” nested OLS models A–E on log price, all covariates standardized, HC3 robust errors; joint F-tests; quantile regressions; bootstrap; winsorization; VIF; Lasso/Elastic-Net selection; PCA benchmark. The 20 dimensions span six domains: **Quality & Standing** (Luxury, Entry-Level) Β· **Condition** (Renovated, Needs Renovation) Β· **Physical** (Bright & Spacious, Garage & Parking, Finished Basement, Pool & Landscaping) Β· **Location & Setting** (Land & Nature, Panoramic View, Premium Location, Quiet & Peaceful, Waterfront) Β· **Style & Character** (Modern/Contemporary, Heritage/Character, New Construction, Energy Efficient, Family-Friendly) Β· **Market Signals** (Income/Investment, Motivated Seller). --- ## πŸ“ Repository Structure ``` wp2_uqo/ β”œβ”€β”€ README.md ← you are here β”œβ”€β”€ AUDIT.md ← forensic audit of the original (pre-restructuring) project β”œβ”€β”€ CHANGES.md ← everything that was moved / refactored / corrected, with diffs β”œβ”€β”€ PAPER_REVIEW.md ← critical pre-upgrade assessment of the paper's scholarship β”œβ”€β”€ UPGRADE_REPORT.md ← literature expansion: refs added/replaced, claims flagged β”œβ”€β”€ requirements.txt ← pinned dependencies (exact versions used for verification) β”‚ β”œβ”€β”€ data/ ← NOT tracked by git (see data/README.md) β”‚ β”œβ”€β”€ raw/louka.db β”‚ SQLite, 268 MB, 46,479 listings (5 categories) β”‚ └── processed/ β”‚ parquet sample, embeddings cache, similarity matrix, β”‚ └── legacy/ β”‚ analysis CSV + pre-refactor artifacts kept for comparison β”‚ β”œβ”€β”€ src/ ← analysis package (imported by scripts/) β”‚ β”œβ”€β”€ config.py β”‚ repo-relative paths, model name, seed, structural vars β”‚ β”œβ”€β”€ references.py β”‚ the 20 French reference descriptions + English labels β”‚ β”œβ”€β”€ data.py β”‚ SQLite β†’ DataFrame extraction (n = 17,087) β”‚ β”œβ”€β”€ embeddings.py β”‚ encoding, caching, cosine-similarity features β”‚ └── models.py β”‚ standardized-design OLS A–E, HC3 β”‚ β”œβ”€β”€ scripts/ ← numbered pipeline entry points (run in order) β”‚ β”œβ”€β”€ 01_prepare_data.py β”‚ louka.db β†’ data/processed/houses.parquet β”‚ β”œβ”€β”€ 02_similarities.py β”‚ embeddings (cached) + 20 similarity features β†’ CSV β”‚ β”œβ”€β”€ 03_models.py β”‚ models A–E β†’ results/*.csv β”‚ β”œβ”€β”€ 04_robustness.py β”‚ VIF Β· BP Β· quantile Β· winsor Β· bootstrap Β· Lasso/ENet β”‚ β”œβ”€β”€ 05_figures.py β”‚ figures fig1–fig11 (PDF + PNG) β”‚ └── 06_tables.py β”‚ LaTeX tables generated from results (no hand transcription) β”‚ β”œβ”€β”€ figures/ ← all 11 paper figures, regenerated from the pipeline β”œβ”€β”€ results/ ← model & robustness outputs (CSV, versioned) β”‚ └── paper/ ← LaTeX source (UQO working-paper style) β”œβ”€β”€ main.tex β”‚ metadata + section inputs (53-page PDF) β”œβ”€β”€ preamble.tex β”‚ packages, layout, custom macros β”œβ”€β”€ main.pdf β”‚ compiled paper (committed for convenience) β”œβ”€β”€ Makefile β”‚ latexmk build β”œβ”€β”€ references.bib β”‚ 86 entries, every one verified (OpenAlex + DOI) β”œβ”€β”€ sections/ β”‚ titlepage Β· intro Β· literature Β· methodology Β· data Β· β”‚ β”‚ results Β· robustness Β· discussion Β· conclusion Β· appendix └── tables/ β”‚ 7 machine-generated + 3 hand-maintained booktabs tables ``` --- ## βš™οΈ The Pipeline, Script by Script | # | Script | Input | Output | Runtime* | |---|---|---|---|---| | 1 | `01_prepare_data.py` | `data/raw/louka.db` | `houses.parquet` (17,087 Γ— 14) | ~15 s | | 2 | `02_similarities.py` | parquet + refs | embeddings `.npy` (cached), `sim_matrix.npy`, analysis CSV | ~5 s cached / ~3 min fresh | | 3 | `03_models.py` | analysis CSV | `model_comparison.csv`, coefficient tables, descriptive stats | ~5 s | | 4 | `04_robustness.py` | analysis CSV | `robustness_results.csv` (163 rows: VIF, BP, QR, winsor, bootstrap, Lasso) | ~4 min (bootstrap) | | 5 | `05_figures.py` | analysis CSV | `figures/fig1…fig11.{pdf,png}` | ~30 s | | 6 | `06_tables.py` | `results/*.csv` | `paper/tables/*.tex` (7 tables) | ~2 s | *Apple M-series laptop. All stochastic steps are seeded (`seed = 42`): bootstrap resampling, CV folds, scatter subsampling. **Design principles applied during the refactor** (see `CHANGES.md` for the full story): - 🚫 **No hardcoded paths** β€” everything resolves from the repo root (`src/config.py`); the DB location can be overridden with `WP2_DB_PATH`. - 🧬 **Single source of truth** β€” the extraction logic and the 20 reference texts, previously copy-pasted across five scripts (in two languages), now live in exactly one place each. - πŸ” **Deterministic & cached** β€” embeddings are deterministic for a given model version and cached; `--force` re-encodes from scratch. - πŸ“‹ **Tables are compiled artifacts** β€” every number in the paper's statistical tables is written by `06_tables.py` from the results CSVs. The original project transcribed numbers by hand, which introduced errors that this restructuring caught and fixed (documented, with oldβ†’new values, in `CHANGES.md` Β§4). --- ## πŸ’Ύ Data The raw data is a **SQLite database (`louka.db`, 268 MB)** of Quebec residential listings collected from Realtor.ca / Centris: **46,479 listings** across five categories β€” houses (17,089), condos (8,586), land (8,506), rentals (8,240), plexes (4,058). Each row carries structured fields (price, bedrooms, bathrooms) plus a full JSON blob including the agent-written `PublicRemarks`. **The analysis sample**: single-family houses with a positive price and a description of β‰₯ 20 characters β†’ **n = 17,087**. Median listing price $589,900 (mean $807,259, max $25M). Descriptions average 510 characters and are truncated at ~700 characters in the export (disclosed in the paper). > ⚠️ **`data/` is not tracked by git**: the raw database exceeds GitHub's 100 MB file limit, contains a leftover application `users` table, and the listing texts are proprietary platform content. `data/README.md` explains how to place a copy of `louka.db` to re-run the pipeline from scratch. All *derived, aggregate* outputs (results CSVs, figures, tables, paper) **are** versioned, so every number in the paper is inspectable without the raw data. --- ## πŸ”„ Reproducing Everything ```bash git clone https://github.com/spboucher-ai/wp2_uqo.git cd wp2_uqo python3 -m pip install -r requirements.txt # place louka.db in data/raw/ (see data/README.md), then: python3 scripts/01_prepare_data.py # extract the house sample python3 scripts/02_similarities.py # embed + project (add --force to re-encode) python3 scripts/03_models.py # hedonic models A–E python3 scripts/04_robustness.py # full robustness battery (~4 min) python3 scripts/05_figures.py # all 11 figures python3 scripts/06_tables.py # LaTeX tables from results cd paper && latexmk -pdf main.tex # 53-page PDF, 0 errors ``` Expected checkpoints along the way: - Step 1 prints `17,087 houses with a valid price and description`. - Step 3 prints the model table with `RΒ² = 0.4525 / 0.4647 / 0.4962 / 0.5122 / 0.5118` and `F = 99.53`. - Step 4 prints `Winsorized R2=0.4867` and the Lasso selection (4/20 at the CV penalty). --- ## πŸ“ Building the Paper The paper (`paper/`) follows the UQO working-paper format: title page with logo, abstract page with keywords + JEL codes, IMRaD body (Introduction Β· Literature Β· Methodology Β· Data Β· Results Β· Robustness Β· Discussion Β· Conclusion), `natbib`/`apalike` bibliography, and two appendices (verbatim reference texts; parsimonious-model estimates). ```bash cd paper make # or: latexmk -pdf main.tex ``` Figures are pulled from `../figures/` via `\graphicspath`, so the paper always reflects the latest pipeline run. The build is clean: **0 errors, 0 undefined references, 0 missing citations** (59/59 bib keys resolve). --- ## βœ… Verification & Provenance This repository is a restructuring of an earlier research folder. Before anything was rewritten, the pipeline was validated against the original artifacts: | Check | Result | |---|---| | Analysis dataset (17,087 Γ— 30) | βœ… identical, column by column | | Similarity matrix (17,087 Γ— 20) | βœ… bit-identical | | Models A–E fit statistics | βœ… identical to the paper's Table 4 | | Model D coefficients (27 params) | βœ… identical to the paper's Table 5 | | Figures fig1, fig4–fig11 | βœ… regenerated, byte-size within Β±2 B | | Figures fig2, fig3 | πŸ”§ were *missing* from the original repo (placeholder boxes in the old PDF) β€” regenerated | | `robustness_results.csv` | πŸ”§ lost in the original β€” regenerated (seeded) | | Descriptive/robustness numbers in the old LaTeX | ⚠️ several did not match the code's own outputs β€” corrected and now machine-generated; full oldβ†’new list in [`CHANGES.md`](CHANGES.md) Β§4 | `AUDIT.md` documents the original project's layout, dead files, and defects; `CHANGES.md` documents every move, refactor, and numeric correction. --- ## πŸ–ΌοΈ Figures Gallery | # | Figure | What it shows | |---|---|---| | 1 | `fig1_model_comparison` | RΒ² / adj. RΒ² across models A–E | | 2 | `fig2_coefficient_plot` | Forest plot of the 20 semantic implicit prices (Model D) | | 3 | `fig3_similarity_distributions` | Box plots of the 20 cosine-similarity features | | 4 | `fig4_quintile_heatmap` | Mean similarity by price quintile Γ— dimension | | 5 | `fig5_correlation_matrix` | Inter-dimension Pearson correlations (0.66–0.97) | | 6 | `fig6_scatter_plots` | Four illustrative similarity–price scatters | | 7 | `fig7_methodology` | Pipeline diagram | | 8 | `fig8_r2_decomposition` | Stacked decomposition of Model D's RΒ² | | 9 | `fig9_price_distribution` | Price and log-price histograms | | 10 | `fig10_structural_coefficients` | Structural coefficients with 95 % CIs | | 11 | `fig11_residual_diagnostics` | Residuals vs. fitted + normal Q-Q | All figures exist as publication PDF (vector) and PNG (300 dpi). --- ## πŸ“¦ Requirements | Package | Version | Used for | |---|---|---| | Python | 3.14 | β€” | | numpy | 2.4.4 | linear algebra | | pandas | 3.0.2 | data wrangling | | pyarrow | 24.0.0 | parquet I/O | | scipy | 1.17.1 | tests, distributions | | scikit-learn | 1.6.1 | PCA, Lasso/ElasticNet CV, scaling | | statsmodels | 0.14.6 | OLS/HC3, QuantReg, BP test, VIF | | matplotlib | 3.10.9 | figures | | sentence-transformers | 5.5.0 | embeddings | | torch | 2.12.0 | transformer backend (MPS on Apple Silicon) | LaTeX: TeX Live 2026 with `latexmk` (newtx, booktabs, threeparttable, natbib, hyperref…). --- ## πŸ“š Citation ```bibtex @techreport{boucher2026decoding, author = {Boucher, Simon-Pierre}, title = {Decoding Real Estate Descriptions: Semantic Embeddings and Hedonic Pricing of Residential Properties in Quebec}, institution = {Universit\'e du Qu\'ebec en Outaouais, D\'epartement des sciences administratives}, type = {Working Paper}, number = {2}, year = {2026}, month = {May} } ``` --- ## πŸ‘€ Author & Contact **Simon-Pierre Boucher** DΓ©partement des sciences administratives UniversitΓ© du QuΓ©bec en Outaouais (UQO) 283, boulevard Alexandre-TachΓ©, Gatineau (QuΓ©bec) J9A 1L8, Canada πŸ“§ **contact@spboucher.ai** *Comments and suggestions are welcome. All errors are my own.* ---
Β© 2026 Simon-Pierre Boucher. Paper, code, and figures β€” all rights reserved.