// UQO Éval — modélisation hédonique : spécification (module PUR, partagé client/serveur). export type Source = "sales" | "listings"; export type AreaMode = "quebec" | "muni" | "radius"; export type DepVar = "price" | "lnprice"; export type TimeFE = "none" | "year" | "quarter" | "month"; export type GeoFE = "none" | "muni" | "arrond" | "voisinage" | "grid1" | "grid2" | "grid5"; export type Method = "ols" | "huber" | "ridge" | "lasso" | "lad" | "sar"; export type SeType = "classic" | "hc1" | "cluster"; /** Variables explicatives disponibles. */ export type VarKey = | "aire" // aire d'étages (m²) — rôle | "terrain" // superficie du terrain (m²) | "age" // âge au moment de la vente | "nb_logements" | "nb_etages" | "type" // type de propriété (catégoriel) | "genre" // genre de construction (plain-pied, à étages…) | "lien" // lien physique (détaché, jumelé, en rangée…) | "dist_centre" // distance au centroïde de la municipalité (km) | "valeur_role" // évaluation municipale (attention : quasi-tautologique) | "bedrooms" // annonces seulement | "bathrooms" // annonces seulement | "trend_xy"; // surface de tendance spatiale (lat, lng, quadratique) export interface VarDef { key: VarKey; fr: string; en: string; kind: "cont" | "cat" | "block"; logable: boolean; sources: Source[]; hint: { fr: string; en: string }; } export const VARS: VarDef[] = [ { key: "aire", fr: "Aire d'étages (m²)", en: "Floor area (m²)", kind: "cont", logable: true, sources: ["sales", "listings"], hint: { fr: "Surface brute des étages hors sous-sol (rôle MAMH). La variable hédonique reine.", en: "Gross floor area excl. basement (MAMH roll). The queen hedonic variable." } }, { key: "terrain", fr: "Superficie du terrain (m²)", en: "Lot area (m²)", kind: "cont", logable: true, sources: ["sales", "listings"], hint: { fr: "Rendement décroissant attendu → forme log recommandée.", en: "Diminishing returns expected → log form recommended." } }, { key: "age", fr: "Âge du bâtiment (ans)", en: "Building age (years)", kind: "cont", logable: false, sources: ["sales", "listings"], hint: { fr: "Année de la vente − année de construction. Ajoutez âge² pour capter la courbure (vintage).", en: "Sale year − year built. Add age² to capture curvature (vintage effect)." } }, { key: "nb_logements", fr: "Nombre de logements", en: "Number of dwellings", kind: "cont", logable: false, sources: ["sales", "listings"], hint: { fr: "Pertinent pour les plex.", en: "Relevant for plexes." } }, { key: "nb_etages", fr: "Nombre d'étages", en: "Storeys", kind: "cont", logable: false, sources: ["sales", "listings"], hint: { fr: "Colinéaire avec le genre de construction.", en: "Collinear with construction style." } }, { key: "type", fr: "Type de propriété", en: "Property type", kind: "cat", logable: false, sources: ["sales", "listings"], hint: { fr: "Variables indicatrices (base = type le plus fréquent).", en: "Dummy variables (base = most frequent type)." } }, { key: "genre", fr: "Genre de construction", en: "Construction style", kind: "cat", logable: false, sources: ["sales", "listings"], hint: { fr: "Plain-pied, à étages entiers, niveaux décalés, mansardé…", en: "Bungalow, full storeys, split-level, mansard…" } }, { key: "lien", fr: "Lien physique", en: "Physical link", kind: "cat", logable: false, sources: ["sales", "listings"], hint: { fr: "Détaché, jumelé, en rangée, intégré.", en: "Detached, semi-detached, row, integrated." } }, { key: "dist_centre", fr: "Distance au centre (km)", en: "Distance to centre (km)", kind: "cont", logable: true, sources: ["sales", "listings"], hint: { fr: "Distance au centroïde des unités de la municipalité — gradient de rente foncière (Alonso-Muth).", en: "Distance to the municipality's centroid — land-rent gradient (Alonso-Muth)." } }, { key: "bedrooms", fr: "Chambres (annonce)", en: "Bedrooms (listing)", kind: "cont", logable: false, sources: ["listings"], hint: { fr: "Déclaré par le vendeur.", en: "Declared by the seller." } }, { key: "bathrooms", fr: "Salles de bain (annonce)", en: "Bathrooms (listing)", kind: "cont", logable: false, sources: ["listings"], hint: { fr: "Déclaré par le vendeur.", en: "Declared by the seller." } }, { key: "trend_xy", fr: "Surface de tendance spatiale", en: "Spatial trend surface", kind: "block", logable: false, sources: ["sales", "listings"], hint: { fr: "Polynôme quadratique en latitude/longitude (5 termes) — alternative lisse aux effets fixes géo.", en: "Quadratic polynomial in lat/lng (5 terms) — smooth alternative to geo fixed effects." } }, { key: "valeur_role", fr: "Valeur au rôle ($)", en: "Assessed value ($)", kind: "cont", logable: true, sources: ["sales", "listings"], hint: { fr: "⚠ Quasi-tautologique : le rôle est lui-même une estimation. À utiliser pour une étude de ratios, pas pour un modèle hédonique structurel.", en: "⚠ Quasi-tautological: the roll is itself an estimate. Use for ratio studies, not a structural hedonic model." } }, ]; export interface HedonicSpec { source: Source; area: { mode: AreaMode; muni?: string; radiusKm?: number }; types: string[]; // sales : unifamilial | plex | condo | indéterminé ; annonces : mêmes clés (maison→unifamilial) period: { from: string; to: string }; // YYYY-MM (ventes) — ignoré pour les annonces price: { min: number; max: number; trimPct: number }; maxN: number; seed: number; depvar: DepVar; vars: VarKey[]; logVars: VarKey[]; ageSquared: boolean; fe: { time: TimeFE; geo: GeoFE }; method: Method; se: SeType; cv: boolean; compareLab: boolean; } export const DEFAULT_SPEC: HedonicSpec = { source: "sales", area: { mode: "muni", muni: "Gatineau" }, types: ["unifamilial"], period: { from: "2021-01", to: "2026-12" }, price: { min: 50000, max: 3000000, trimPct: 1 }, maxN: 6000, seed: 42, depvar: "lnprice", vars: ["aire", "terrain", "age", "lien", "genre"], logVars: ["aire", "terrain"], ageSquared: true, fe: { time: "quarter", geo: "none" }, method: "ols", se: "hc1", cv: true, compareLab: true, }; export const METHODS: { key: Method; fr: string; en: string; hint: { fr: string; en: string } }[] = [ { key: "ols", fr: "Moindres carrés ordinaires (MCO)", en: "Ordinary least squares (OLS)", hint: { fr: "Le point de départ de toute analyse hédonique (Rosen, 1974). Inférence complète.", en: "The starting point of any hedonic analysis (Rosen, 1974). Full inference." } }, { key: "huber", fr: "Régression robuste (Huber, IRLS)", en: "Robust regression (Huber, IRLS)", hint: { fr: "Réduit l'influence des ventes atypiques (prix hors marché, erreurs de saisie).", en: "Downweights atypical sales (non-market prices, data errors)." } }, { key: "lad", fr: "Régression quantile — médiane (LAD)", en: "Quantile regression — median (LAD)", hint: { fr: "Modélise le prix médian conditionnel ; insensible aux queues. Sans écarts-types.", en: "Models the conditional median price; tail-insensitive. No standard errors." } }, { key: "ridge", fr: "Ridge (pénalité L2, λ par validation croisée)", en: "Ridge (L2 penalty, CV λ)", hint: { fr: "Stabilise les coefficients quand les variables sont colinéaires (aire, étages, logements…).", en: "Stabilises coefficients under collinearity (area, storeys, dwellings…)." } }, { key: "lasso", fr: "LASSO (pénalité L1, λ par validation croisée)", en: "LASSO (L1 penalty, CV λ)", hint: { fr: "Sélectionne automatiquement les variables ; inférence post-LASSO sur les variables retenues.", en: "Automatic variable selection; post-LASSO inference on kept variables." } }, { key: "sar", fr: "Modèle spatial autorégressif (SAR, 2SLS)", en: "Spatial autoregressive model (SAR, 2SLS)", hint: { fr: "Ajoute le prix des k voisins (ρ·Wy) — dépendance spatiale explicite ; instruments WX, W²X (Kelejian-Prucha).", en: "Adds neighbours' prices (ρ·Wy) — explicit spatial dependence; WX, W²X instruments (Kelejian-Prucha)." } }, ]; export const SE_TYPES: { key: SeType; fr: string; en: string }[] = [ { key: "classic", fr: "Classiques (homoscédastiques)", en: "Classical (homoskedastic)" }, { key: "hc1", fr: "Robustes à l'hétéroscédasticité (HC1, White)", en: "Heteroskedasticity-robust (HC1, White)" }, { key: "cluster", fr: "Groupées par zone géographique (cluster)", en: "Clustered by geographic zone" }, ]; export const TIME_FE: { key: TimeFE; fr: string; en: string }[] = [ { key: "none", fr: "Aucun", en: "None" }, { key: "year", fr: "Année", en: "Year" }, { key: "quarter", fr: "Trimestre", en: "Quarter" }, { key: "month", fr: "Mois", en: "Month" }, ]; export const GEO_FE: { key: GeoFE; fr: string; en: string }[] = [ { key: "none", fr: "Aucun", en: "None" }, { key: "muni", fr: "Municipalité", en: "Municipality" }, { key: "arrond", fr: "Arrondissement / secteur", en: "Borough / sector" }, { key: "voisinage", fr: "Unité de voisinage (rôle)", en: "Neighbourhood unit (roll)" }, { key: "grid1", fr: "Grille 1 km", en: "1 km grid" }, { key: "grid2", fr: "Grille 2 km", en: "2 km grid" }, { key: "grid5", fr: "Grille 5 km", en: "5 km grid" }, ]; export const TYPE_OPTIONS: { key: string; fr: string; en: string }[] = [ { key: "unifamilial", fr: "Unifamiliale", en: "Single-family" }, { key: "condo", fr: "Condo", en: "Condo" }, { key: "plex", fr: "Plex", en: "Plex" }, { key: "indéterminé", fr: "Indéterminé (ventes)", en: "Undetermined (sales)" }, ]; export const LIMITS = { maxN: 25000, minN: 60, maxGeoLevels: 160, maxCols: 320 };