SPB Git forge

spb/uqo-eval

Public
55commits 2branches 0releases
134.4 MBsize
maindefault branch
yesterdaylast push
TypeScript 90.1% JavaScript 3.2% Python 3.2% CSS 1.8% HTML 1.8%
9.4 KB · 131 lines typescript
Raw Blame History
1// UQO Éval — modélisation hédonique : spécification (module PUR, partagé client/serveur).23export type Source = "sales" | "listings";4export type AreaMode = "quebec" | "muni" | "radius";5export type DepVar = "price" | "lnprice";6export type TimeFE = "none" | "year" | "quarter" | "month";7export type GeoFE = "none" | "muni" | "arrond" | "voisinage" | "grid1" | "grid2" | "grid5";8export type Method = "ols" | "huber" | "ridge" | "lasso" | "lad" | "sar";9export type SeType = "classic" | "hc1" | "cluster";1011/** Variables explicatives disponibles. */12export type VarKey =13  | "aire" // aire d'étages (m²) — rôle14  | "terrain" // superficie du terrain (m²)15  | "age" // âge au moment de la vente16  | "nb_logements"17  | "nb_etages"18  | "type" // type de propriété (catégoriel)19  | "genre" // genre de construction (plain-pied, à étages…)20  | "lien" // lien physique (détaché, jumelé, en rangée…)21  | "dist_centre" // distance au centroïde de la municipalité (km)22  | "valeur_role" // évaluation municipale (attention : quasi-tautologique)23  | "bedrooms" // annonces seulement24  | "bathrooms" // annonces seulement25  | "trend_xy"; // surface de tendance spatiale (lat, lng, quadratique)2627export interface VarDef {28  key: VarKey;29  fr: string;30  en: string;31  kind: "cont" | "cat" | "block";32  logable: boolean;33  sources: Source[];34  hint: { fr: string; en: string };35}3637export const VARS: VarDef[] = [38  { 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." } },39  { 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." } },40  { 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)." } },41  { 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." } },42  { 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." } },43  { 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)." } },44  { 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…" } },45  { 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." } },46  { 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)." } },47  { 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." } },48  { 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." } },49  { 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." } },50  { 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." } },51];5253export interface HedonicSpec {54  source: Source;55  area: { mode: AreaMode; muni?: string; radiusKm?: number };56  types: string[]; // sales : unifamilial | plex | condo | indéterminé ; annonces : mêmes clés (maison→unifamilial)57  period: { from: string; to: string }; // YYYY-MM (ventes) — ignoré pour les annonces58  price: { min: number; max: number; trimPct: number };59  maxN: number;60  seed: number;61  depvar: DepVar;62  vars: VarKey[];63  logVars: VarKey[];64  ageSquared: boolean;65  fe: { time: TimeFE; geo: GeoFE };66  method: Method;67  se: SeType;68  cv: boolean;69  compareLab: boolean;70}7172export const DEFAULT_SPEC: HedonicSpec = {73  source: "sales",74  area: { mode: "muni", muni: "Gatineau" },75  types: ["unifamilial"],76  period: { from: "2021-01", to: "2026-12" },77  price: { min: 50000, max: 3000000, trimPct: 1 },78  maxN: 6000,79  seed: 42,80  depvar: "lnprice",81  vars: ["aire", "terrain", "age", "lien", "genre"],82  logVars: ["aire", "terrain"],83  ageSquared: true,84  fe: { time: "quarter", geo: "none" },85  method: "ols",86  se: "hc1",87  cv: true,88  compareLab: true,89};9091export const METHODS: { key: Method; fr: string; en: string; hint: { fr: string; en: string } }[] = [92  { 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." } },93  { 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)." } },94  { 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." } },95  { 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…)." } },96  { 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." } },97  { 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)." } },98];99100export const SE_TYPES: { key: SeType; fr: string; en: string }[] = [101  { key: "classic", fr: "Classiques (homoscédastiques)", en: "Classical (homoskedastic)" },102  { key: "hc1", fr: "Robustes à l'hétéroscédasticité (HC1, White)", en: "Heteroskedasticity-robust (HC1, White)" },103  { key: "cluster", fr: "Groupées par zone géographique (cluster)", en: "Clustered by geographic zone" },104];105106export const TIME_FE: { key: TimeFE; fr: string; en: string }[] = [107  { key: "none", fr: "Aucun", en: "None" },108  { key: "year", fr: "Année", en: "Year" },109  { key: "quarter", fr: "Trimestre", en: "Quarter" },110  { key: "month", fr: "Mois", en: "Month" },111];112113export const GEO_FE: { key: GeoFE; fr: string; en: string }[] = [114  { key: "none", fr: "Aucun", en: "None" },115  { key: "muni", fr: "Municipalité", en: "Municipality" },116  { key: "arrond", fr: "Arrondissement / secteur", en: "Borough / sector" },117  { key: "voisinage", fr: "Unité de voisinage (rôle)", en: "Neighbourhood unit (roll)" },118  { key: "grid1", fr: "Grille 1 km", en: "1 km grid" },119  { key: "grid2", fr: "Grille 2 km", en: "2 km grid" },120  { key: "grid5", fr: "Grille 5 km", en: "5 km grid" },121];122123export const TYPE_OPTIONS: { key: string; fr: string; en: string }[] = [124  { key: "unifamilial", fr: "Unifamiliale", en: "Single-family" },125  { key: "condo", fr: "Condo", en: "Condo" },126  { key: "plex", fr: "Plex", en: "Plex" },127  { key: "indéterminé", fr: "Indéterminé (ventes)", en: "Undetermined (sales)" },128];129130export const LIMITS = { maxN: 25000, minN: 60, maxGeoLevels: 160, maxCols: 320 };131