\documentclass[11pt,a4paper]{article} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage[french]{babel} \usepackage{lmodern} \usepackage{amsmath,amssymb} \usepackage{booktabs} \usepackage{longtable} \usepackage{array} \usepackage{geometry} \usepackage{xcolor} \usepackage{enumitem} \usepackage{hyperref} \usepackage{fancyhdr} \geometry{margin=2.4cm} \hypersetup{colorlinks=true, linkcolor=blue!55!black, urlcolor=blue!55!black, citecolor=blue!55!black} \newcommand{\code}[1]{\texttt{\small #1}} \renewcommand{\arraystretch}{1.25} \pagestyle{fancy} \fancyhf{} \lhead{\small Modèle hédonique de prix — Québec} \rhead{\small\thepage} \renewcommand{\headrulewidth}{0.4pt} \title{\textbf{Méthodologie}\\[2mm] \large Modèle hédonique de prix des maisons (Québec)\\[1mm] \normalsize Realtor.ca $+$ DuProprio $\rightarrow$ nettoyage $\rightarrow$ ingénierie de variables (règles $+$ LLM $+$ vision) $\rightarrow$ régression hédonique log-linéaire à effets fixes} \author{} \date{Données extraites le 3 juillet 2026} \begin{document} \maketitle \thispagestyle{fancy} \begin{abstract} \noindent Ce document décrit la méthodologie complète de construction d'un modèle hédonique de prix pour les maisons unifamiliales et maisons en rangée du Québec. Le pipeline combine deux jeux de données d'annonces immobilières, un nettoyage des valeurs aberrantes fondé sur des règles, une ingénierie de variables à quatre origines (règles déterministes, extraction de texte par grand modèle de langage, analyse d'images par modèle de vision, effets fixes géographiques) et une estimation par moindres carrés ordinaires du logarithme du prix avec erreurs-types robustes à l'hétéroscédasticité. \end{abstract} \tableofcontents \bigskip %====================================================================== \section{Origine des données} \begin{longtable}{@{}p{2.4cm}p{5.2cm}rp{4.6cm}@{}} \toprule \textbf{Source} & \textbf{Fichier} & \textbf{Nb} & \textbf{Nature}\\ \midrule \endhead Realtor.ca & \code{dataset\_realtor-\dots.json} (161 Mo) & 27\,302 & Annonces MLS via crawler Apify\\ DuProprio & \code{dataset\_duproprio-\dots.json} (87 Mo) & 7\,693 & Annonces de vente sans intermédiaire (Apify)\\ \bottomrule \end{longtable} \begin{itemize}[leftmargin=1.2em] \item \textbf{Collecte} : scrapers Apify, extraction le \textbf{2026-07-03}. Chaque enregistrement est un objet JSON structuré. \item \textbf{Champs communs} : \code{source}, \code{url}, \code{listingId}, \code{title}, \code{price}/\code{priceNumber}, \code{currency}, \code{address\{...\}}, \code{property\{...\}}, \code{features[]}, \code{description}, \code{images[]}, \code{scrapedAt}. \item \textbf{Spécificités} : \begin{itemize}[leftmargin=1.2em] \item Realtor : \code{buildingType}, \code{storeys}, \code{yearBuilt}, \code{parkingSpaces}, \textbf{\code{rooms[]}} (dimensions par pièce) ; pas de coordonnées GPS ; images sans légende utile. \item DuProprio : \code{type}, \code{levels}, \textbf{\code{coordinates}} (7\,691 présentes) ; images \textbf{avec légendes} (\emph{Kitchen}, etc.) ; pas de \code{rooms}. \end{itemize} \item \textbf{Instantané transversal} (une seule date) : pas de dimension temporelle, donc pas d'effet fixe temporel requis. \end{itemize} \noindent\textbf{Identifiant unique} : \code{uid = source + ":" + listingId} (évite toute collision entre les deux espaces d'identifiants). %====================================================================== \section{Construction du dataset «~maisons~»} \begin{enumerate}[leftmargin=1.4em] \item \textbf{Fusion} des deux tableaux JSON (schéma harmonisé). \item \textbf{Définition de «~maison~»} (unifamiliales $+$ maisons en rangée) : \begin{itemize}[leftmargin=1.2em] \item Realtor : \code{buildingType} $\in \{$House, Row/Townhouse$\}$ ; \item DuProprio : \code{type} $\in \{$Home, Bungalow, Semi Detached, Intergenerational, Country Home, Cottage, Villa, Townhouse$\}$ ; \item \emph{Exclus} : condos, plex (duplex$\rightarrow$quintuplex), terrains/lots, fermes, commercial, maisons mobiles. \end{itemize} \item \textbf{Filtre prix présent} : \code{priceNumber} non nul et $>0$. \end{enumerate} \noindent$\rightarrow$ \textbf{19\,273 maisons avec prix} (15\,470 Realtor $+$ 3\,803 DuProprio). %====================================================================== \section{Nettoyage des valeurs aberrantes} Les prix extraits contiennent des erreurs d'analyse syntaxique (minimum observé 899~\$, maximum 368~G\$). Règles appliquées : \begin{longtable}{@{}p{3.0cm}p{6.2cm}p{5.2cm}@{}} \toprule \textbf{Variable} & \textbf{Règle} & \textbf{Justification}\\ \midrule \endhead Prix & garder \textbf{50\,k\$ -- 15\,M\$} & plage plausible au QC ; retire loyers mal analysés et concaténations\\ Surface habitable & 300 -- 20\,000 ft$^2$ (sinon $\rightarrow$ manquant) & valeurs physiquement possibles\\ Terrain & 200 -- 5\,000\,000 ft$^2$ (sinon $\rightarrow$ manquant) & idem\\ Chambres / sdb & 0--15 / 0--12 (sinon $\rightarrow$ manquant) & plafonds réalistes\\ Prix/pied$^2$ & garder \textbf{P0{,}5 -- P99{,}5} ($\approx 73$--$1276~\$$/ft$^2$) & retire les erreurs résiduelles de prix ou surface\\ \bottomrule \end{longtable} \noindent$\rightarrow$ \textbf{19\,115 maisons} conservées (158 retirées). \paragraph{Principe.} On \emph{borne} plutôt que de supprimer quand une seule variable est aberrante : la variable est mise à \emph{manquant} puis imputée, pour ne pas perdre l'observation entière. %====================================================================== \section{Construction des variables} \subsection{Variable cible} \[ \code{ln\_price} = \ln(\code{priceNumber}) \] La transformation logarithmique rend la distribution symétrique, linéarise les effets multiplicatifs et donne des coefficients interprétables en pourcentage d'impact sur le prix. \subsection{Variables structurelles (règles déterministes)} \begin{itemize}[leftmargin=1.2em] \item \textbf{Surface \& terrain} : analyse d'unités hétérogènes, tout converti en \textbf{pieds carrés} : \code{"1535 sqft"}, \code{"1,333.2 ft²"}, \code{"11 561.52 ft²"} (séparateurs de milliers variés), \code{"961.6 m2"} ($\times 10{,}7639$). \item \textbf{Reconstruction de surface via les pièces (Realtor)} : dimensions \code{"23 ft ,4 in x 13 ft ,8 in"} $\rightarrow$ pieds décimaux $\rightarrow$ aire, sommées sur toutes les pièces. Calibration sur le recouvrement (ratio médian surface réelle / somme des pièces $\approx 0{,}98$) pour combler \code{squareFeet} manquant. \item \textbf{Autres} : \code{beds}, \code{baths}, \code{parking}, \code{storeys}/\code{levels}, \code{age} $= 2026 - \code{yearBuilt}$ (avec \code{age}$^2$ pour la courbure), drapeaux \code{has\_*} marquant les valeurs imputées. \end{itemize} \subsection{Aménités (règles par mots-clés bilingues)} 17 variables indicatrices détectées dans \code{features[]} $+$ \code{description} $+$ \code{title} (FR/EN) : piscine, bord de l'eau, garage, foyer, climatisation/ thermopompe, sous-sol fini, cul-de-sac, golf, ski, proximité parc/école/autoroute/ transport, absence de voisin arrière. \subsection{Détail des pièces (Realtor)} Nombre de pièces, nombre de niveaux, chambre principale, sous-sol, bureau, salle familiale, atelier, cellier à vin, solarium, mezzanine, bibliothèque, buanderie. \subsection{Signaux de mise en marché} Nombre de photos (\code{n\_images}, écrêté à 60), présence de visite virtuelle — indicateurs indirects de gamme et de qualité de l'annonce. \subsection{Variables géographiques (effets fixes)} \begin{itemize}[leftmargin=1.2em] \item \textbf{Ville} nettoyée (Realtor : retrait du quartier entre parenthèses ; minuscules ; normalisation). Villes avec $\ge 20$ annonces $\rightarrow$ indicatrice propre, les rares $\rightarrow$ \code{\_\_other\_\_} ($\approx 194$ villes en effets fixes). \item \textbf{Type de propriété} harmonisé, regroupé si $<30$ annonces. \item \textbf{Source} (Realtor/DuProprio) $\rightarrow$ indicatrice captant les différences systématiques entre plateformes. \end{itemize} \subsection{Imputation} Valeurs manquantes après nettoyage : imputées par \textbf{médiane par type de propriété} (puis médiane globale), \textbf{avec un drapeau \code{has\_*}} pour que le modèle distingue valeur observée et valeur imputée. %====================================================================== \section{Mécanismes d'IA pour l'ingénierie de variables} Deux couches d'IA extraient des variables impossibles à obtenir par règles~: l'une depuis le texte, l'autre depuis les images. \subsection{Extraction de specs depuis la description --- GPT-4o-mini} \begin{itemize}[leftmargin=1.2em] \item \textbf{But} : transformer la description libre en 20 attributs structurés (état~: à rénover$\rightarrow$neuf ; clé en main ; cuisine/sdb rénovée ; revêtements de sol ; comptoirs quartz/granit ; chauffage ; vue sur l'eau ; revenus locatifs ; grand terrain ; secteur tranquille ; mots-clés de vente). \item \textbf{Mécanisme} : appel \emph{Chat Completions} avec \code{response\_format = json\_schema} \textbf{strict} (schéma imposé, énumérations fermées) $\rightarrow$ sortie JSON valide garantie, sans analyse fragile. \code{temperature = 0} (déterminisme). Instruction système : extracteur factuel, uniquement d'après le texte ; absence $\rightarrow$ false/inconnu/[]. \item \textbf{Ingénierie} : $\sim$600 tokens en entrée / $\sim$110 en sortie par annonce ; \textbf{parallèle} (24 fils), \textbf{résumable} (cache \code{specs.jsonl}, clé \code{uid}), \textbf{réessais} exponentiels sur 429/5xx. \item \textbf{Coût / temps} : $\sim$\$3 pour $\sim$19\,k annonces. \end{itemize} \subsection{Attributs visuels de façade --- Claude Haiku 4.5 (vision)} \begin{itemize}[leftmargin=1.2em] \item \textbf{But} : lire ce que le texte ne dit pas --- état visuel réel, attrait extérieur (\emph{curb appeal}), niveau de rénovation, matériaux, aménagement paysager, cadre (urbain/banlieue/rural/bord d'eau/boisé), indice de luxe, qualité de la photo. \item \textbf{Sélection d'image} : une photo/annonce $=$ la façade. Realtor $\rightarrow$ 1\up{re} photo ; DuProprio $\rightarrow$ photo dont la légende est l'adresse (pas une pièce), par exclusion de mots-clés de pièces. \item \textbf{Mécanisme} : \emph{Messages API}, image passée par \textbf{URL} (\code{source.type = url}, sans téléchargement), \code{output\_config.format = json\_schema} (12 attributs, énumérations fermées). Instruction système : évaluateur objectif ; scores fondés sur des critères concrets (surfaces, matériaux, aménagement), pas sur un goût personnel. \item \textbf{Ingénierie} : $\sim$2\,470 tokens en entrée / $\sim$100 en sortie par image ; \textbf{parallèle} (16 fils), \textbf{résumable} (cache \code{facade\_specs.jsonl}), \textbf{réessais} sur 429/5xx/529. \item \textbf{Coût / temps} : $\sim$\$57 pour $\sim$19\,k images. \item \textbf{Choix de Haiku} : modèle de vision le moins cher et rapide, suffisant pour de la classification d'attributs concrets. \end{itemize} \subsection{Rôle central du JSON structuré strict} Le schéma imposé (types $+$ \code{enum} $+$ champs requis $+$ \code{additionalProperties:false}) fait valider la sortie \textbf{au niveau de l'API}~: le modèle renvoie exactement les champs attendus, dans le bon domaine de valeurs. On obtient des variables directement tabulables et reproductibles, sans post-traitement de texte libre. \subsection{Intégration au dataset} Les sorties des modèles (clé \code{uid}) sont converties en variables numériques~: booléens $\rightarrow$ 0/1, état $\rightarrow$ score ordinal 0--5, revêtements $\rightarrow$ indicatrices (bois franc, quartz/granit), vue $\rightarrow$ indicatrice eau, etc., avec un drapeau \code{has\_gpt} / \code{is\_exterior} marquant la disponibilité. %====================================================================== \section{Modèle économétrique} \subsection{Spécification --- hédonique log-linéaire à effets fixes} \begin{equation} \ln(P_i) = \alpha + \beta\,\ln(\text{surface}_i) + \gamma\,\ln(\text{terrain}_i) + \sum_k \delta_k X_{ki} + \sum_c \lambda_c\,\mathbb{1}[\text{ville}_i=c] + \varepsilon_i \end{equation} \begin{itemize}[leftmargin=1.2em] \item \textbf{Log--log} sur surface et terrain $\rightarrow$ coefficients $=$ \textbf{élasticités} (\% de prix par \% de surface). \item \textbf{Log--niveau} sur le reste $\rightarrow$ un coefficient $b$ vaut $\approx (e^{b}-1)\times 100$~\% d'impact sur le prix. \item \textbf{Effets fixes ville} ($\lambda_c$) : absorbent le différentiel de localisation (premier déterminant du prix) ; chaque coefficient $=$ prime/décote de la ville vs la référence. \item \textbf{Effets fixes} type de propriété et source. \end{itemize} \subsection{Double spécification (couverture vs richesse)} Realtor ne rapporte \code{squareFeet} que pour $\sim$38~\% des annonces, d'où deux modèles et des prédictions combinées~: \begin{itemize}[leftmargin=1.2em] \item \textbf{Modèle A (riche)} : inclut $\ln(\text{surface})$, estimé sur les biens à surface \emph{mesurée} (réelle ou reconstruite via les pièces). Meilleur ajustement. \item \textbf{Modèle B (large)} : sans surface, estimé sur \emph{toutes} les maisons $\rightarrow$ price celles sans surface (beds/baths/terrain/pièces $+$ specs). \item \textbf{Prédiction finale} : A si surface mesurée, sinon B. \end{itemize} \subsection{Estimation et inférence} \begin{itemize}[leftmargin=1.2em] \item \textbf{Moindres carrés ordinaires} sur \code{ln\_price}. \item \textbf{Erreurs-types robustes à l'hétéroscédasticité HC3} (les résidus immobiliers sont hétéroscédastiques ; HC3 est conservateur en échantillon moyen). \item \textbf{Correction du biais de rétrotransformation} : \[ \hat{P} = e^{\widehat{\ln P}} \times e^{\hat\sigma^2/2} \] (correction normale de Duan) --- sinon $e^{\widehat{\ln P}}$ sous-estime systématiquement le niveau de prix. \end{itemize} \subsection{Interprétation} Coefficients rapportés en \textbf{\% d'impact prix} $+$ significativité (p-values sur SE HC3). Effets fixes ville présentés comme primes de localisation. %====================================================================== \section{Statistiques et diagnostics} \begin{longtable}{@{}p{5.2cm}p{9.4cm}@{}} \toprule \textbf{Indicateur} & \textbf{Signification}\\ \midrule \endhead $R^2$ / $R^2$ ajusté & part de variance de \code{ln\_price} expliquée\\ Écart-type des résidus (log) & $\rightarrow$ erreur typique en \% $=(e^{\sigma}-1)\times100$\\ Erreur absolue médiane & $|\,$prix réel $/$ prix prédit $-1\,|$ médian\\ \% à $\pm15$~\% / $\pm25$~\% & part des prédictions dans la fourchette\\ \code{resid\_pct} par annonce & surprix/sous-prix vs modèle $\rightarrow$ repère aubaines et surévaluations\\ \bottomrule \end{longtable} \noindent Progression observée : v2 (structurel) $R^2\approx0{,}70$ $\rightarrow$ v3 ($+$ pièces/photos) $R^2\approx0{,}72$ $\rightarrow$ v4 ($+$ specs GPT) $\rightarrow$ v5 ($+$ vision) attendu. %====================================================================== \section{Limites et biais} \begin{itemize}[leftmargin=1.2em] \item \textbf{Endogénéité / non-causalité} : de belles photos ou des specs riches corrèlent avec des courtiers haut de gamme et des biens chers $\rightarrow$ coefficients \emph{prédictifs}, pas causaux. \item \textbf{Biais de sélection} : uniquement des biens \emph{en vente} (prix demandé, pas prix de transaction) ; les biens sans surface/pièces sont non aléatoires (annonces moins complètes). \item \textbf{Imputation} : les médianes par type réduisent la variance des variables imputées (atténuation possible des coefficients), d'où les drapeaux \code{has\_*}. \item \textbf{Extraction LLM} : erreurs/hallucinations possibles ; \code{temperature=0} $+$ schéma strict $+$ énumérations fermées les limitent, sans validation humaine systématique. La vision juge sur \emph{une seule} photo. \item \textbf{Instantané unique} : pas de dynamique de marché ; primes de localisation figées au 2026-07-03. \item \textbf{Couverture géographique} : villes $<20$ annonces regroupées en \code{\_\_other\_\_}. \end{itemize} %====================================================================== \section{Fichiers produits} \begin{longtable}{@{}p{5.4cm}p{9.2cm}@{}} \toprule \textbf{Fichier} & \textbf{Contenu}\\ \midrule \endhead \code{hedonic.py}, \code{hedonic\_v4.py} & pipelines reproductibles (nettoyage $+$ modèles)\\ \code{extract\_specs.py}, \code{facade\_vision.py} & extracteurs LLM (texte / vision), parallèles et résumables\\ \code{specs.jsonl}, \code{facade\_specs.jsonl} & sorties LLM brutes (clé \code{uid})\\ \code{maisons\_hedonic*.csv} & dataset final $+$ prix prédit $+$ \code{resid\_pct}\\ \code{hedonic\_*\_summary.txt} & sorties statistiques complètes\\ \bottomrule \end{longtable} \end{document}