SPB Git

spb/wp5_uqo Public

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

TeX 53.4% Python 46.5%
17.8 KB · 321 lines markdown
Rendered Raw Blame History
1<!-- Author: Simon-Pierre Boucher — contact@spboucher.ai -->23<div align="center">45# 🏠 Airbnb, Residential Rents & Housing Market Pressure67### *A Hedonic and Spatial Econometric Analysis*89**UQO Working Paper No. 5**1011<br>1213[![Author](https://img.shields.io/badge/Author-Simon--Pierre%20Boucher-1f6feb?style=for-the-badge)](mailto:contact@spboucher.ai)14[![Institution](https://img.shields.io/badge/UQO-Sciences%20administratives-00457C?style=for-the-badge)](https://uqo.ca)15[![Contact](https://img.shields.io/badge/✉️-contact%40spboucher.ai-EA4335?style=for-the-badge)](mailto:contact@spboucher.ai)1617<br>1819**— The paper at a glance —**2021[![PDF](https://img.shields.io/badge/📄_PDF-51_pages-success?style=flat-square)](paper/main.pdf)22[![References](https://img.shields.io/badge/📚_References-56_(all_DOI--verified)-blueviolet?style=flat-square)]()23[![Sample](https://img.shields.io/badge/🏘️_Sample-8,303_rentals_·_3,456_Airbnb-informational?style=flat-square)]()24[![Headline](https://img.shields.io/badge/β-%2B0.4%25_rent_per_listing_(500m)-e63946?style=flat-square)]()25[![Tables](https://img.shields.io/badge/📊-13_tables-teal?style=flat-square)]()26[![Figures](https://img.shields.io/badge/📈-16_figures-teal?style=flat-square)]()27[![Build](https://img.shields.io/badge/latexmk-0_unresolved_refs-brightgreen?style=flat-square)]()2829**— Methods —**3031[![OLS](https://img.shields.io/badge/Hedonic-OLS_·_HC1_·_city_FE-orange?style=flat-square)]()32[![Spatial](https://img.shields.io/badge/Spatial-SAR_·_SEM_·_GMM_(spreg)-8A2BE2?style=flat-square)]()33[![Quantile](https://img.shields.io/badge/Quantile-τ_∈_{.10….90}-c44536?style=flat-square)]()34[![ML](https://img.shields.io/badge/ML-LASSO_·_ENet_·_RF_·_GBM_·_SHAP-F7931E?style=flat-square)]()35[![Geo](https://img.shields.io/badge/Geospatial-Haversine_buffers_250m→2km-2a9d8f?style=flat-square)]()3637**— Stack —**3839[![Python](https://img.shields.io/badge/Python-3.14-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/)40[![pandas](https://img.shields.io/badge/pandas-3.0.2-150458?style=flat-square&logo=pandas&logoColor=white)](https://pandas.pydata.org/)41[![statsmodels](https://img.shields.io/badge/statsmodels-0.14.6-4051B5?style=flat-square)](https://www.statsmodels.org/)42[![scikit-learn](https://img.shields.io/badge/scikit--learn-1.6.1-F7931E?style=flat-square&logo=scikitlearn&logoColor=white)](https://scikit-learn.org/)43[![PySAL](https://img.shields.io/badge/PySAL%2Fspreg-1.9.0-8A2BE2?style=flat-square)](https://pysal.org/)44[![SHAP](https://img.shields.io/badge/SHAP-0.48.0-FF4081?style=flat-square)](https://shap.readthedocs.io/)45[![LaTeX](https://img.shields.io/badge/LaTeX-latexmk-008080?style=flat-square&logo=latex&logoColor=white)](https://www.latex-project.org/)4647**— Quality —**4849[![Reproducible](https://img.shields.io/badge/pipeline-100%25_reproducible-brightgreen?style=flat-square)]()50[![Verified](https://img.shields.io/badge/tables-14%2F16_byte--identical_to_originals-brightgreen?style=flat-square)]()51[![Deterministic](https://img.shields.io/badge/seed-42_·_deterministic-lightgrey?style=flat-square)]()52[![Status](https://img.shields.io/badge/status-working_paper_v1.0-blue?style=flat-square)]()5354</div>5556---5758## 🧭 Navigation5960| | | |61|---|---|---|62| [📜 The Paper](#-the-paper) | [🔍 Abstract](#-abstract) | [📖 Inside the Paper](#-inside-the-paper--section-by-section) |63| [🏆 Headline Results](#-headline-results) | [📁 Repository Map](#-repository-map) | [💾 Data](#-data) |64| [⚙️ Pipeline](#️-analysis-pipeline) | [📐 Models](#-econometric-models) | [🛡️ Robustness](#️-robustness-programme) |65| [🚀 Reproduce](#-reproducing-everything) | [🧰 Environment](#-environment--dependencies) | [✅ Provenance](#-verification--provenance) |66| [📖 Citation](#-citation) | [📬 Contact](#-contact) | |6768---6970## 📜 The Paper7172<div align="center">7374> ### **Airbnb, Residential Rents, and Housing Market Pressure:**75> ### **A Hedonic and Spatial Econometric Analysis**76>77> **Simon-Pierre Boucher** — Université du Québec en Outaouais78> *UQO Working Paper No. 5 · May 2026 · 51 pages · 56 references*79>80> [![Read](https://img.shields.io/badge/▶_READ_THE_PAPER-paper%2Fmain.pdf-b31b1b?style=for-the-badge)](paper/main.pdf)8182</div>8384## 🔍 Abstract8586> This paper investigates the relationship between **Airbnb short-term rental activity** and **residential rents** in Quebec, Canada. Using cross-sectional microdata comprising ~5,000 Airbnb listings and ~8,300 rental listings, we employ a **hedonic pricing framework** augmented with **spatial econometric techniques** to quantify the conditional association between nearby Airbnb presence and monthly rents. For each rental listing we construct Airbnb exposure measures within **250 m, 500 m, 1 km, and 2 km buffers** using Haversine distances. Baseline estimates indicate that an additional Airbnb listing within 500 m is associated with a statistically significant increase in monthly rent of approximately **0.3–0.5%**, controlling for dwelling characteristics, building type, and city fixed effects. **Quantile regressions** reveal that the association is stronger at the upper tail of the rent distribution. A complementary hedonic model of Airbnb nightly prices indicates that short-term rental pricing is driven primarily by listing characteristics. Robustness checks — alternative buffer radii, subsample analyses, and machine-learning benchmarks — confirm the stability of the rent–exposure association. We discuss policy implications for housing affordability and short-term rental regulation, while cautioning that **cross-sectional associations should not be interpreted as causal effects**.87>88> **Keywords:** Airbnb · short-term rentals · housing rents · hedonic pricing · spatial econometrics · housing affordability · Quebec89> **JEL:** R21 · R31 · L83 · C219091---9293## 📖 Inside the Paper — Section by Section9495| § | Section | What it delivers | Key exhibits |96|:-:|---|---|---|97| **1** | 🎬 **Introduction** | Motivation, the P2P-platform housing channel, the Canadian evidence gap, magnitude preview vs. Berlin/LA quasi-experiments, three contributions | — |98| **2** | 📚 **Literature Review** | 10 thematic subsections: correlational → quasi-experimental STR evidence, Canadian & Quebec studies, P2P economics, STR pricing, hedonic theory, spatial econometrics, quantile methods, ML in housing, tourism gentrification & regulation, gap statement | 56 refs |99| **3** | 💾 **Data** | Two scraped microdatasets, cleaning protocol, **Haversine spatial buffer merge** (6 exposure metrics × 4 radii), summary statistics, geography | Tables 1–2 · Figures 1–3 |100| **4** | 📐 **Methodology** | Six models: hedonic rent (1a–1e), Airbnb pricing (2a–2c), city-level (3), SAR/SEM via Kelejian–Prucha GMM (4), quantile (5), ML benchmark (6); identification & endogeneity discussion | Equations 1–6 |101| **5** | 📊 **Results** | β = 0.0039*** per nearby listing; spatial dependence (ρ̂ = 0.137***, λ̂ = 0.539); quantile gradient .0037→.0047; superhost *discount*; RF test R² = 0.71 | Tables 3–9 · Figures 4–7 |102| **6** | 🛡️ **Robustness** | Buffer decay, alternative exposures, subsamples, trimming, **city-clustered SEs**, size control, log form, **ring decomposition**, **leave-one-city-out** | Tables 10–12 · Figures 8–10 |103| **7** | 💬 **Discussion** | Magnitudes benchmarked against Berlin ($6–10 ≈ 7–13 €/listing), LA & Boston; two candid disagreements with the literature; policy for CITQ/Montreal zoning; 7 limitations; future work | — |104| **8** | 🎯 **Conclusion** | Five findings, cross-design corroboration, no overselling | — |105| **A–C** | 📎 **Appendices** | Variable dictionary, city-name standardisation, sample attrition · Haversine & KNN-weights formulas, estimator details · supplementary robustness | Tables A1–A2 |106107---108109## 🏆 Headline Results110111<div align="center">112113[![F1](https://img.shields.io/badge/1-%2B0.4%25_rent_per_Airbnb_listing_within_500m-e63946?style=for-the-badge)]()114[![F2](https://img.shields.io/badge/2-Spatial_decay:_0.93%25_→_0.06%25_(250m_→_2km)-f4a261?style=for-the-badge)]()115[![F3](https://img.shields.io/badge/3-Strongest_at_the_top:_β(τ=.90)=0.0047-e9c46a?style=for-the-badge)]()116[![F4](https://img.shields.io/badge/4-Survives_SAR%2FSEM_(ρ̂=0.137***)-2a9d8f?style=for-the-badge)]()117[![F5](https://img.shields.io/badge/5-No_city--rent_premium_in_nightly_prices-264653?style=for-the-badge)]()118[![F6](https://img.shields.io/badge/6-Stable_in_leave--one--city--out_(incl._Montreal)-6d597a?style=for-the-badge)]()119120</div>121122| # | Finding | Evidence |123|:-:|---|---|124| 1 | Each additional Airbnb listing within **500 m****≈ +0.4%** monthly rent (0.3–0.5% across specs) — ≈ **$6–10/month** at the median rent, the same order as Berlin's quasi-experimental 7–13 €/listing | Table 3 (1c) · §7.1 |125| 2 | Per-listing association **decays monotonically with distance** | Table 10 · Fig. 4 |126| 3 | Significant at **every quantile**, rising to **0.0047 at τ = 0.90** (~25% above OLS) | Table 7 · Fig. 5 |127| 4 | **Spatial dependence is substantial** and the coefficient survives GMM spatial models with mild attenuation (0.0034) | Table 6 |128| 5 | Airbnb nightly prices driven by **listing characteristics**; superhost enters with a *discount* (vs. premia in prior work — discussed, not hidden) | Table 4 · §7.1 |129| 6 | Robust to **city-clustered SEs** (tighter than HC1), size control, log(1+count), ring decomposition, trimming, and dropping any of the 8 largest cities | Table 12 · Fig. 9 |130131---132133## 📁 Repository Map134135```136wp5_uqo/137├── 📄 README.md                    ← you are here138├── 🔎 AUDIT.md                     ← forensic audit of the original project139├── 📝 CHANGES.md                   ← restructuring & rewrite log140├── 🧪 PAPER_REVIEW.md              ← pre-upgrade critical assessment141├── 📚 UPGRADE_REPORT.md            ← literature expansion report (per-ref justification)142├── 📦 requirements.txt             ← pinned dependencies143144├── data/145│   ├── raw/                        ← airbnb.csv + rent.json (NOT distributed → README)146│   └── processed/                  ← 5 verified parquet datasets (committed)147148├── src/                            ← shared modules149│   ├── config.py                   ← all paths & constants150│   ├── geo.py                      ← vectorised Haversine matrix151│   ├── latex_tables.py             ← stargazer-style table builder152│   └── plotting.py                 ← publication matplotlib styles153154├── scripts/                        ← 01 → 11 numbered pipeline155├── figures/                        ← 16 publication PDF figures156├── results/tables/                 ← 13 LaTeX fragments + 4 CSVs157├── results/logs/                   ← data-inspection log158159└── paper/                          ← LaTeX → 📄 main.pdf (51 pages)160    ├── main.tex · references.bib (56 entries) · Makefile161    ├── sections/  (titlepage + 8 sections)162    └── appendix/  (data · methods · robustness)163```164165---166167## 💾 Data168169<div align="center">170171[![Airbnb](https://img.shields.io/badge/Airbnb-3,456_cleaned_listings-FF5A5F?style=flat-square&logo=airbnb&logoColor=white)]()172[![Realtor](https://img.shields.io/badge/Realtor.ca-8,303_cleaned_rentals-006AFF?style=flat-square)]()173[![Merge](https://img.shields.io/badge/Spatial_merge-24_exposure_variables-2a9d8f?style=flat-square)]()174[![Coverage](https://img.shields.io/badge/Coverage-Province_of_Quebec-1d3557?style=flat-square)]()175176</div>177178| Dataset | Source | Raw → Clean | Key fields |179|---|---|---|---|180| 🛏️ Airbnb listings | Airbnb (scraped) | ~5,000 → **3,456** | nightly price, lat/lon, property type, rating, reviews, superhost |181| 🏢 Rental listings | Realtor.ca (scraped) | 8,356 → **8,303** | monthly rent, lat/lon, bedrooms, bathrooms, building type, size |182183**Spatial merge** — for every rental listing × radius *r* ∈ {250 m, 500 m, 1 km, 2 km}, six metrics computed against all Airbnb listings via chunked vectorised Haversine distances: `count` · `density` · `mean_price` · `share_entire_home` · `mean_rating` · `superhost_share`, plus city-level aggregates.184185> 🔒 Raw files are **not distributed** (`data/raw/README.md`). The committed parquets make steps 04–11 and the paper fully reproducible without them.186187---188189## ⚙️ Analysis Pipeline190191| Step | Script | Produces | ⏱ |192|:-:|---|---|:-:|193| 01 | `01_inspect_raw_data.py` | data dictionary log | ⚡ |194| 02 | `02_clean_airbnb.py` | `airbnb_clean.parquet` | ⚡ |195| 03 | `03_clean_rent.py` | `rent_clean.parquet` | ⚡ |196| 04 | `04_merge_data.py` | 3 merged parquets (buffer merge) | ~1 min |197| 05 | `05_descriptive_analysis.py` | 3 tables + 7 figures | ⚡ |198| 06 | `06_hedonic_models.py` | Models 1–3 → 3 tables | ⚡ |199| 07 | `07_spatial_models.py` | SAR/SEM + buffer robustness | ~1 min |200| 08 | `08_quantile_models.py` | quantile table + coefficient path | ~1 min |201| 09 | `09_ml_robustness.py` | ML comparison + SHAP figures | ~2 min |202| 10 | `10_robustness_tables_figures.py` | outlier/subsample tables + 3 figures | ⚡ |203| 11 | `11_extended_robustness.py` | clustered SEs · ring · leave-one-out | ~1 min |204205<sub>⚡ = under 15 s on Apple Silicon. Everything reads/writes through `src/config.py`; fully deterministic (seed 42).</sub>206207---208209## 📐 Econometric Models210211| Model | Specification | Estimator |212|:-:|---|---|213| **1a–1e** | `log_rent ~ airbnb_count_500m + X + building type [+ city FE]`; variants: density, entire-home share | OLS · HC1 |214| **2a–2c** | `log_price ~ mean_rent_city + listing controls [+ city FE]` | OLS · HC1 |215| **3** | city-level forward & reverse regressions (153 cities) | OLS · HC1 |216| **4** | SAR & SEM, KNN(k=5) row-standardised weights | GMM — `GM_Lag` / `GM_Error` (Kelejian–Prucha) |217| **5** | quantile regressions, τ ∈ {.10, .25, .50, .75, .90} + fine grid | IRLS (statsmodels) |218| **6** | OLS / LASSO / ENet / RF / GBM, 80/20 split, SHAP attribution | scikit-learn |219220---221222## 🛡️ Robustness Programme223224<div align="center">225226[![R1](https://img.shields.io/badge/☑-4_buffer_radii-2a9d8f?style=flat-square)]()227[![R2](https://img.shields.io/badge/☑-alternative_exposures-2a9d8f?style=flat-square)]()228[![R3](https://img.shields.io/badge/☑-4_subsamples-2a9d8f?style=flat-square)]()229[![R4](https://img.shields.io/badge/☑-outlier_trims-2a9d8f?style=flat-square)]()230[![R5](https://img.shields.io/badge/☑-city--clustered_SEs-2a9d8f?style=flat-square)]()231[![R6](https://img.shields.io/badge/☑-size_control-2a9d8f?style=flat-square)]()232[![R7](https://img.shields.io/badge/☑-log(1%2Bcount)-2a9d8f?style=flat-square)]()233[![R8](https://img.shields.io/badge/☑-ring_decomposition-2a9d8f?style=flat-square)]()234[![R9](https://img.shields.io/badge/☑-leave--one--city--out-2a9d8f?style=flat-square)]()235236</div>237238The Airbnb coefficient stays **positive and significant at 1%** in every single check; the leave-one-city-out range is 0.0038–0.0040 — excluding Montreal (half the sample) barely moves it.239240---241242## 🚀 Reproducing Everything243244```bash245git clone https://github.com/spboucher-ai/wp5_uqo.git && cd wp5_uqo246python3 -m pip install -r requirements.txt247248# full pipeline (steps 01–03 optional: need data/raw/)249for s in scripts/0{4..9}_*.py scripts/1{0,1}_*.py; do python3 "$s"; done250251cd paper && latexmk        # → paper/main.pdf  (51 pages)252```253254---255256## 🧰 Environment & Dependencies257258Verified on **macOS (Apple Silicon) · Python 3.14.4 · TeX Live 2025**.259260| Package | Pinned | Role |261|---|:-:|---|262| pandas / pyarrow | 3.0.2 / 24.0.0 | wrangling, parquet I/O |263| numpy / scipy | 2.4.4 / 1.17.1 | Haversine, KD-tree |264| statsmodels | 0.14.6 | OLS (HC1/cluster), quantile regression |265| libpysal / spreg | 4.14.1 / 1.9.0 | KNN weights, SAR/SEM (GMM) |266| scikit-learn | 1.6.1 | LASSO, ENet, RF, GBM |267| shap | 0.48.0 | SHAP values |268| matplotlib | 3.10.9 | all figures |269270---271272## ✅ Verification & Provenance273274Rebuilt on **2026-08-05** from the original project (kept untouched as backup) under a strict *no-result-changes* policy:275276- ✅ 3/3 merged parquets **value-identical** to the originals277-**14/16 tables byte-identical** (2 residual float-level diffs documented in [`AUDIT.md`](AUDIT.md) §8)278- ✅ All figures regenerated, including 5 missing from the original (which broke its LaTeX build)279- ✅ Every text↔table inconsistency corrected *in the prose only* — itemised in [`CHANGES.md`](CHANGES.md)280- ✅ All 32 references added in the scholarly upgrade **verified via Crossref/JMLR** (DOIs) — per-reference justification in [`UPGRADE_REPORT.md`](UPGRADE_REPORT.md)281282---283284## 📖 Citation285286```bibtex287@techreport{boucher2026airbnb,288  author      = {Boucher, Simon-Pierre},289  title       = {Airbnb, Residential Rents, and Housing Market Pressure:290                 A Hedonic and Spatial Econometric Analysis},291  institution = {Universit\'e du Qu\'ebec en Outaouais,292                 D\'epartement des sciences administratives},293  type        = {Working Paper},294  number      = {5},295  year        = {2026},296  month       = {May},297  address     = {Gatineau, QC, Canada}298}299```300301---302303## 📬 Contact304305<div align="center">306307**Simon-Pierre Boucher**308Département des sciences administratives · Université du Québec en Outaouais309283, boulevard Alexandre-Taché, Gatineau (Québec) J9A 1L8, Canada310311[![Email](https://img.shields.io/badge/✉️_contact@spboucher.ai-primary-EA4335?style=for-the-badge)](mailto:contact@spboucher.ai)312[![UQO](https://img.shields.io/badge/🏛️_simon--pierre.boucher@uqo.ca-00457C?style=for-the-badge)](mailto:simon-pierre.boucher@uqo.ca)313314<sub>Issues and PRs welcome — please do not request the raw scraped data, which cannot be redistributed.</sub>315316<br><br>317318<sub>© 2026 Simon-Pierre Boucher — UQO Working Paper No. 5 · Code and manuscript for academic use.</sub>319320</div>321