Montréal : lire les transformations locales
Le contexte change la lecture d’un indicateur
TERRIQ · Étude achevée · Observations jusqu’en 2025 · Révision septembre 2026
Le récit commence à Lachine : des logements autorisés, une population en hausse, puis une réoccupation commerciale concentrée sur Notre-Dame. Ahuntsic montre une hausse de population avec davantage de locaux vacants. Le Plateau présente une réoccupation importante malgré une faible hausse démographique antérieure.
Ce notebook reprend les résultats validés et vérifie les pièces qui les soutiennent. Population : 2016–2021. Locaux commerciaux : 2021–2025. L’occupation ne certifie pas des ouvertures ou fermetures d’entreprises, ni leurs revenus. La dernière cellule génère le rapport LaTeX français.
Exécuter le notebook
Python 3.9 ou plus récent, avec pandas, matplotlib et ipykernel. Décompressez l’archive de reproduction, ouvrez Jupyter dans research-deliverables/ et exécutez les cellules dans l’ordre. Les résultats sont déjà enregistrés pour la lecture sans noyau. Les entrées sont lues sans modification ; la dernière cellule écrit uniquement montreal_neighbourhood_change_fr.tex.
Les figures originales validées sont conservées. Leurs légendes d’origine sont en anglais ; les explications et les nouveaux tableaux de présentation sont en français.
Afficher le code Python · cellule 1
from pathlib import Path
from collections import Counter
import hashlib
import json
import math
import sys
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, Markdown, Image
candidates = [Path.cwd().resolve(), *Path.cwd().resolve().parents]
STUDY_ROOT = next((p for p in candidates if (p / 'PUBLICATION_EVIDENCE.json').is_file()
and (p / 'prototype/assets/study.json').is_file()), None)
if STUDY_ROOT is None:
raise FileNotFoundError('Exécuter le notebook depuis le dossier research-deliverables de l’étude.')
DELIVERABLES = STUDY_ROOT / 'research-deliverables'
ASSETS = STUDY_ROOT / 'prototype/assets'
pd.set_option('display.max_colwidth', 100)
pd.set_option('display.max_rows', 30)
plt.rcParams.update({'figure.dpi': 125, 'font.family': 'DejaVu Sans',
'axes.spines.top': False, 'axes.spines.right': False})
print('Étude locale chargée. Les entrées sont lues sans modification.')Étude locale chargée. Les entrées sont lues sans modification.
Les pièces et leur provenance
Le manifeste consigne les empreintes des entrées analytiques et des figures originales. La cellule contrôle ces fichiers, rapproche la présentation avec les preuves et relit la QA existante. Une entrée modifiée interrompt la vérification.
Afficher le code Python · cellule 2
def read_json(path):
return json.loads(path.read_text(encoding='utf-8'))
def sha256(path):
digest = hashlib.sha256()
with path.open('rb') as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
digest.update(chunk)
return digest.hexdigest()
study = read_json(ASSETS / 'study.json')
evidence = read_json(STUDY_ROOT / 'PUBLICATION_EVIDENCE.json')
manifest = read_json(STUDY_ROOT / 'PUBLICATION_MANIFEST.json')
validation = read_json(STUDY_ROOT / 'PUBLICATION_VALIDATION.json')
assert validation['checks_passed'] == manifest['publication_checks']
assert validation['inputs_preserved'] is True
assert manifest['database_access'] == 'none'
source_checks = []
for relative, expected in manifest['input_sha256'].items():
path = STUDY_ROOT / relative
actual = sha256(path)
assert actual == expected, f'Entrée modifiée : {path.name}'
assert study['source_artifact_sha256'][path.name] == expected
source_checks.append({'Artefact': path.name, 'SHA-256 vérifié': True})
assert sha256(STUDY_ROOT / 'PUBLICATION_EVIDENCE.json') == manifest['publication_files']['PUBLICATION_EVIDENCE.json']['sha256']
for name, recorded in manifest['figures'].items():
assert sha256(ASSETS / 'figures' / name) == recorded['sha256'], name
for source in study['methodology_sources']['files']:
assert sha256(ASSETS / 'methodology' / source['path']) == source['sha256']
cases = study['cases']
assert [c['tract_id'] for c in cases] == manifest['case_ids']
for c in cases:
for key, expected in evidence[c['tract_id']].items():
actual = c[key]
if isinstance(expected, float):
assert math.isclose(actual, expected, abs_tol=0.000001), (c['name'], key)
else:
assert actual == expected, (c['name'], key)
for key, expected in evidence['verdun'].items():
assert study['verdun'][key] == expected, ('Verdun', key)
# Presentation-specific additions are independently checked against raw records.
raw_permits = pd.read_csv(STUDY_ROOT / 'data/deep_dive/permit_timeline.csv', dtype=str, keep_default_na=False).to_dict('records')
for record in study['verdun']['galt_records']:
assert any(all(raw[k] == value for k, value in record.items()) for raw in raw_permits)
review = pd.read_csv(STUDY_ROOT / 'data/deep_dive/baseline_CO_description_review.csv', dtype=str, keep_default_na=False)
review = review[(review['tract_id'] == '4620305.00') & (review['housing_label_broad'] == 'True')]
classes = review['description_review_class'].value_counts()
assert len(review) == study['verdun']['housing_CO']
assert classes['principal_housing_construction_described'] == study['verdun']['principal_housing_descriptions']
assert classes['excavation_related_to_principal_building'] == study['verdun']['excavation_descriptions']
assert classes['other_work_no_new_principal_building_described'] == study['verdun']['other_descriptions']
assert study['relationship'] == evidence['relationships'][1]
print(f"Vérification : {len(source_checks)} empreintes analytiques, {len(manifest['figures'])} figures originales et 3 PDF de méthode.")
print(f"QA de publication existante : {validation['checks_passed']} contrôles et {validation['quantitative_claim_groups']} groupes de chiffres.")
display(pd.DataFrame(source_checks))Vérification : 17 empreintes analytiques, 12 figures originales et 3 PDF de méthode. QA de publication existante : 301 contrôles et 79 groupes de chiffres.
| Artefact | SHA-256 vérifié | |
|---|---|---|
| 0 | relationships.csv | True |
| 1 | tract_analysis.parquet | True |
| 2 | baseline_CO_description_review.csv | True |
| 3 | boundary_context_occupancy.csv | True |
| 4 | commercial_category_composition.csv | True |
| 5 | commercial_records.csv | True |
| 6 | endpoint_premise_reconciliation.csv | True |
| 7 | floor_comparable_category_composition.csv | True |
| 8 | floor_comparable_stock.csv | True |
| 9 | major_project_evidence.csv | True |
| 10 | matched_transition_detail.csv | True |
| 11 | permit_timeline.csv | True |
| 12 | stock_panel_decomposition.csv | True |
| 13 | tracts_2021.geojson | True |
| 14 | source_manifest.json | True |
| 15 | targeted_assessment_join_diagnostics.json | True |
| 16 | official_borough_boundaries.geojson | True |
Population, 2016–2021
Deux recensements, sur la géographie préparée de l’étude. Ils ne décrivent ni migration annuelle ni date d’arrivée individuelle. L’arithmétique vérifie la variation publiée ; elle n’ajoute pas de conclusion analytique.
Afficher le code Python · cellule 3
population = pd.DataFrame([{
'Dossier': c['name'], 'Secteur 2021': c['tract_id'],
'Résidents 2016': c['population_2016'], 'Résidents 2021': c['population_2021'],
'Variation de population (%)': c['population_change_pct'],
} for c in cases]).set_index('Dossier')
for c in cases:
calculated = 100 * (c['population_2021'] - c['population_2016']) / c['population_2016']
assert math.isclose(calculated, c['population_change_pct'], abs_tol=1e-10)
display(population.style.format({'Variation de population (%)': '{:+.1f}'}))
display(Image(filename=str(ASSETS / 'figures/overview_map.png'), width=1000))| Secteur 2021 | Résidents 2016 | Résidents 2021 | Variation de population (%) | |
|---|---|---|---|---|
| Dossier | ||||
| Lachine | 4620390.00 | 1198 | 1603 | +33.8 |
| Ahuntsic | 4620270.00 | 4529 | 4739 | +4.6 |
| Plateau | 4620142.00 | 1657 | 1683 | +1.6 |
Carte : secteurs étudiés. Figure originale validée. Géographie : Statistique Canada ; contexte administratif actuel : Ville de Montréal. Le rapport d’aspect géographique est conservé. Les secteurs sont des unités analytiques à l’intérieur des quartiers, pas des zones de clientèle.
Les mêmes locaux, de 2021 à 2025
Le panel compare des locaux identifiables aux deux extrémités. Son dénominateur diffère de l’inventaire enquêté. Une réoccupation part de l’ensemble initialement vacant ; une transition vers la vacance, de l’ensemble initialement occupé. Des points de pourcentage d’occupation ne sont pas une croissance des revenus.
Afficher le code Python · cellule 4
panel = pd.DataFrame([{
'Dossier': c['name'], 'Locaux appariés n': c['panel_n'],
'Occupés 2021': c['panel_occupied_2021'], 'Vacants 2021': c['panel_vacant_2021'],
'Occupés 2025': c['panel_occupied_2025'], 'Vacants 2025': c['panel_vacant_2025'],
'Vacant → occupé': c['vacant_to_occupied'], 'Occupé → vacant': c['occupied_to_vacant'],
'Variation de part occupée (points)': c['panel_occupied_change_pp'],
} for c in cases]).set_index('Dossier')
for c in cases:
n = c['panel_n']
assert c['panel_occupied_2021'] + c['panel_vacant_2021'] == n
assert c['panel_occupied_2025'] + c['panel_vacant_2025'] == n
assert c['panel_occupied_2025'] == c['panel_occupied_2021'] + c['vacant_to_occupied'] - c['occupied_to_vacant']
expected_pp = 100 * (c['panel_occupied_2025'] - c['panel_occupied_2021']) / n
assert math.isclose(expected_pp, c['panel_occupied_change_pp'], abs_tol=1e-10)
assert c['vacant_to_occupied'] <= c['panel_vacant_2021']
assert c['occupied_to_vacant'] <= c['panel_occupied_2021']
display(panel.style.format({'Variation de part occupée (points)': '{:+.1f}'}))
fig, axes = plt.subplots(1, 3, figsize=(12, 4.4), sharex=True)
for ax, c in zip(axes, cases):
n = c['panel_n']
occupied = [100 * c[f'panel_occupied_{year}'] / n for year in [2021, 2025]]
vacant = [100 * c[f'panel_vacant_{year}'] / n for year in [2021, 2025]]
ax.barh([1, 0], occupied, color='#007166', height=.45, label='Occupé')
ax.barh([1, 0], vacant, left=occupied, color='#ead6b0', edgecolor='#8c682c', height=.45, label='Vacant')
ax.set_yticks([1, 0], ['2021', '2025'])
ax.set_xlim(0, 100); ax.set_xticks([0, 50, 100]); ax.set_xlabel('Part des locaux appariés (%)')
ax.set_title(f"{c['name']} · n={n}", loc='left', fontsize=11, fontweight='bold')
ax.text(0, -.28, f"2021: {c['panel_occupied_2021']} occupés / {c['panel_vacant_2021']} {'vacant' if c['panel_vacant_2021'] == 1 else 'vacants'}\n"
f"2025: {c['panel_occupied_2025']} occupés / {c['panel_vacant_2025']} vacants", fontsize=9, va='top')
ax.set_ylim(-.72, 1.5)
handles, labels = axes[0].get_legend_handles_labels()
fig.legend(handles, labels, loc='lower center', ncol=2, frameon=False)
fig.suptitle('Occupation commerciale · locaux appariés, 2021–2025', x=.06, ha='left', fontsize=14)
fig.subplots_adjust(left=.06, right=.98, top=.8, bottom=.2, wspace=.30)
plt.show(); plt.close(fig)| Locaux appariés n | Occupés 2021 | Vacants 2021 | Occupés 2025 | Vacants 2025 | Vacant → occupé | Occupé → vacant | Variation de part occupée (points) | |
|---|---|---|---|---|---|---|---|---|
| Dossier | ||||||||
| Lachine | 25 | 20 | 5 | 23 | 2 | 3 | 0 | +12.0 |
| Ahuntsic | 44 | 43 | 1 | 34 | 10 | 0 | 9 | -20.5 |
| Plateau | 62 | 46 | 16 | 58 | 4 | 12 | 0 | +19.4 |
Sources : enquêtes commerciales de la Ville de Montréal, 2021–2025 ; panels TERRIQ validés. Les décomptes accompagnent les deux observations. Aucun état intermédiaire n’est interpolé.
Inventaire et couverture
L’inventaire comprend des locaux hors du panel. Le périmètre comparable exclut l’étiquette de deuxième étage 2E aux deux dates. Cela réduit une différence connue sans résoudre chaque changement d’identifiant, d’unité, d’étage ou de local physique.
Afficher le code Python · cellule 5
inventory = pd.DataFrame([{
'Dossier': c['name'], 'Tous étages, 2021': c['all_floor_stock_2021'],
'Tous étages, 2025': c['all_floor_stock_2025'],
'Étages comparables, 2021': c['comparable_stock_2021'],
'Étages comparables, 2025': c['comparable_stock_2025'],
} for c in cases]).set_index('Dossier')
display(inventory)| Tous étages, 2021 | Tous étages, 2025 | Étages comparables, 2021 | Étages comparables, 2025 | |
|---|---|---|---|---|
| Dossier | ||||
| Lachine | 41 | 41 | 41 | 41 |
| Ahuntsic | 59 | 52 | 55 | 52 |
| Plateau | 82 | 79 | 79 | 79 |
Lachine : relier les pièces
Observation. Au 380, avenue George-V, les autorisations du 22 octobre 2018 et du 17 juin 2019 décrivent le même projet de 51 logements. Le rôle actuel en consigne 51, avec une année de construction de 2019. La population passe de 1 198 à 1 603. Trois locaux sur Notre-Dame sont ensuite réoccupés.
Interprétation. Développement résidentiel documenté et croissance antérieure coexistent avec une réoccupation commerciale ultérieure localisée.
Question ouverte. L’effet causal des nouveaux résidents et les revenus. Au 300, avenue George-V, le rôle consigne 22 logements et une année de 2022, après le recensement. Le calendrier d’achèvement à Saint-Joseph reste non résolu. Les années foncières ne certifient pas une livraison.
Afficher le code Python · cellule 6
c = next(c for c in cases if c['key'] == 'lachine')
display(Image(filename=str(ASSETS / 'figures/lachine_case.png'), width=1000))
display(pd.DataFrame([{'Corridor': street, 'Locaux appariés changeant d’état': count}
for street, count in c['corridors'].items()]))| Corridor | Locaux appariés changeant d’état | |
|---|---|---|
| 0 | RUE NOTRE-DAME O | 3 |
Figure originale de Lachine. Locaux suivis, points d’autorisation George-V et trois transitions sur Notre-Dame. Au départ : 20 occupés / 5 vacants ; n=25. Commerce : 2021–2025. Deux locaux sont observés occupés pour la première fois le 28 juillet 2022 ; le troisième le 28 août 2023. Ce sont des dates d’enquête, pas d’ouverture. Les points de permis ne sont pas des empreintes immobilières ; les espacements de la chronologie ne représentent pas des durées proportionnelles. Sources : Ville de Montréal ; Statistique Canada ; étude TERRIQ.
Ahuntsic : une trajectoire divergente
Observation. La population passe de 4 529 à 4 739. Le permis Sauvé de 2017 et le rôle actuel identifient 83 logements. Neuf locaux initialement occupés sont vacants à la dernière observation.
Interprétation. Les signaux démographiques et résidentiels positifs coexistent avec davantage de vacance. La correction des étages de l’inventaire n’efface pas ces neuf transitions.
Question ouverte. Loyers, baux, demande, accessibilité et concurrence. Le calendrier du rôle Sauvé et le contexte de livraisons de la SCHL ne concordent pas ; aucun ne certifie l’achèvement du projet.
Afficher le code Python · cellule 7
c = next(c for c in cases if c['key'] == 'ahuntsic')
display(Image(filename=str(ASSETS / 'figures/ahuntsic_case.png'), width=1000))
display(pd.DataFrame([{'Corridor': street, 'Locaux appariés changeant d’état': count}
for street, count in c['corridors'].items()]))| Corridor | Locaux appariés changeant d’état | |
|---|---|---|
| 0 | BOULEVARD SAINT-LAURENT | 5 |
| 1 | RUE LAJEUNESSE | 2 |
| 2 | RUE FOUCHER | 1 |
| 3 | RUE DE PORT-ROYAL E | 1 |
Figure originale d’Ahuntsic. Neuf transitions occupé→vacant et un point d’autorisation Sauvé. Au départ : 43 occupés / 1 vacant ; n=44. Commerce : 2021–2025. Des unités distinctes peuvent partager une adresse. Certaines sont réoccupées entre deux visites : la chronologie n’implique pas un déclin continu. Sources : Ville de Montréal ; Statistique Canada ; étude TERRIQ.
Plateau : ce que la population seule masque
Observation. La population passe de 1 657 à 1 683. Douze locaux initialement vacants sont occupés à la dernière observation, sur Saint-Denis, Rachel et Duluth. Les descriptions de permis révèlent des transformations et des logements aux étages.
Interprétation. Une réoccupation substantielle accompagne une faible hausse démographique antérieure. Les seuls codes de construction neuve masqueraient aussi des transformations résidentielles.
Question ouverte. Causes de la réoccupation, performance financière et achèvement des logements. Un inventaire comparable stable n’implique pas des unités physiques ou identifiants inchangés.
Afficher le code Python · cellule 8
c = next(c for c in cases if c['key'] == 'plateau')
display(Image(filename=str(ASSETS / 'figures/plateau_case.png'), width=1000))
display(pd.DataFrame([{'Corridor': street, 'Locaux appariés changeant d’état': count}
for street, count in c['corridors'].items()]))
# Existing classification result; this is not a measure of sales or employment.
food = pd.DataFrame(study['plateau_food'])[['survey_edition', 'classified_occupied', 'share']]
food = food.rename(columns={'survey_edition': 'Édition de l’enquête', 'classified_occupied': 'Locaux occupés, alimentation et restauration', 'share': 'Part des locaux occupés classifiés'})
display(food.style.format({'Part des locaux occupés classifiés': '{:.1%}'}))| Corridor | Locaux appariés changeant d’état | |
|---|---|---|
| 0 | AVENUE DULUTH E | 2 |
| 1 | RUE SAINT-DENIS | 6 |
| 2 | RUE RACHEL E | 4 |
| Édition de l’enquête | Locaux occupés, alimentation et restauration | Part des locaux occupés classifiés | |
|---|---|---|---|
| 0 | 2021 | 20 | 36.4% |
| 1 | 2025 | 30 | 42.3% |
Figure originale du Plateau. Douze transitions vacant→occupé, trois corridors et transformations sélectionnées. Au départ : 46 occupés / 16 vacants ; n=62. Commerce : 2021–2025. La réoccupation n’est pas attribuée au permis antérieur. La comparaison alimentaire et restauration utilise le périmètre d’étages constant (20→30) ; le total initial tous étages était 21. Ces décomptes ne sont pas des ventes. Sources : Ville de Montréal ; Statistique Canada ; étude TERRIQ.
Autorisations et caractéristiques immobilières datées
Les descriptions originales et les dates soutiennent le récit. Une autorisation de construction ou de transformation ne certifie pas l’achèvement. Le nombre de logements touchés peut concerner un bâtiment existant ou être répété entre autorisations : il ne s’additionne pas comme des logements neufs nets.
Afficher le code Python · cellule 9
permit_rows = []
property_rows = []
for c in cases:
for project in c['projects']:
for record in project['permit_records']:
permit_rows.append({'Dossier': c['name'], 'Adresse': project['address'],
'Date d’autorisation': record['issued_date'], 'Code': record['permit_type_code'],
'Champ brut des logements touchés': record['raw_dwellings_value'],
'Description originale des travaux': record['nature_travaux']})
for record in c['assessments']:
property_rows.append({'Dossier': c['name'], 'Caractéristiques immobilières': record['target'],
'Logements résidentiels consignés': record['current_recorded_dwelling_sum'],
'Années de construction inscrites': ', '.join(y for y in record['construction_years'] if y != '9999') or 'Non résolu',
'État modifié': record['stock_snapshot_last_modified'],
'Consulté': record['retrieved_date'], 'Confiance du rapprochement': record['join_confidence']})
display(pd.DataFrame(permit_rows).sort_values(['Dossier', 'Date d’autorisation']).reset_index(drop=True))
display(pd.DataFrame(property_rows))| Dossier | Adresse | Date d’autorisation | Code | Champ brut des logements touchés | Description originale des travaux | |
|---|---|---|---|---|---|---|
| 0 | Ahuntsic | 500 rue Sauvé Est | 2017-11-21 | CO | 83 | Construire un bâtiment résidentiel contigu de 3 étages, 2 niveaux en sous-sol et 1 construction ... |
| 1 | Lachine | 380 avenue George-V | 2018-10-22 | CO | 0 | 51 condoplex |
| 2 | Lachine | 380 avenue George-V | 2019-06-17 | CO | 0 | 51 condoplex |
| 3 | Lachine | 300 avenue George-V | 2020-11-30 | CO | 22 | Construction d'un immeuble de 5 étages, 22 logenements et 3 niveaux de sous-sol sur le lot numér... |
| 4 | Lachine | 300 avenue George-V | 2020-12-21 | CA | 0 | BRANCHEMENT À L'ÉGOUT ET À L'AQUEDUC DE L'IMMEUBLE DE 5 ÉTAGES (22 LOGEMENTS). Complémentaire po... |
| 5 | Plateau | 752 rue Rachel Est | 2017-04-05 | TR | 8 | Façade Rdc, côté gauche modifier vitrine pour ajout 1 porte en alcôve, remplacer porte centrale.... |
| 6 | Plateau | 752 rue Rachel Est | 2019-10-09 | TR | 0 | Suite au permis 3000640677-17, compléter les travaux aménagement des 8 logements. Aucuns travaux... |
| 7 | Plateau | 4051 rue Saint-Denis | 2021-08-27 | TR | 8 | Au 2e et 3e étage, aménager 8 nouveaux logements. Au toit, ajouter des équipements mécaniques. T... |
| 8 | Plateau | 4051 rue Saint-Denis | 2025-04-25 | TR | 0 | Au RC, réaménager un restaurant |
| Dossier | Caractéristiques immobilières | Logements résidentiels consignés | Années de construction inscrites | État modifié | Consulté | Confiance du rapprochement | |
|---|---|---|---|---|---|---|---|
| 0 | Lachine | 220 boulevard Saint-Joseph | 0 | Non résolu | 2026-09-23 | 2026-09-30 | unresolved_civic_range_parity_no_exact_220 |
| 1 | Lachine | 300 avenue George-V | 22 | 2022 | 2026-09-23 | 2026-09-30 | address_borough_match_no_stock_geometry |
| 2 | Lachine | 380 avenue George-V property group | 51 | 2019 | 2026-09-23 | 2026-09-30 | shared_matricule_prefix_with_380_george_v_no_stock_geometry |
| 3 | Ahuntsic | 500 rue Sauvé Est | 83 | 2019 | 2026-09-23 | 2026-09-30 | address_borough_match_completion_source_conflict |
Historiques et premières observations d’un état différent
Les états par édition sont conservés. « Première observation occupée » ne signifie pas date d’ouverture ; « première observation vacante » ne signifie pas date de fermeture. Les unités distinctes d’une même adresse restent séparées. Seuls les locaux changeant d’état sont listés ; le dénominateur complet accompagne le tableau.
Afficher le code Python · cellule 10
history_rows = []
for c in cases:
counts = Counter(r['transition'] for r in c['transition_premises'])
assert counts['vacant→occupied'] == c['vacant_to_occupied']
assert counts['occupied→vacant'] == c['occupied_to_vacant']
dates = Counter()
for record in c['transition_premises']:
states = record['state_sequence'].split('→')
assert len(states) == 5
assert f'{states[0]}→{states[-1]}' == record['transition']
changed_date = record.get('first_observed_occupied_after_baseline') or record.get('first_observed_vacant_after_baseline')
dates[changed_date[:4]] += 1
history_rows.append({'Dossier': c['name'], 'Locaux appariés n': c['panel_n'],
'Occupés / vacants 2021': f"{c['panel_occupied_2021']} / {c['panel_vacant_2021']}",
'Adresse': record['baseline_address'], 'Étage': record['baseline_floor'],
'Unité': record['baseline_suite'] or 'Non fourni',
**dict(zip(map(str, range(2021, 2026)), states)),
'Première observation du nouvel état': changed_date})
assert dict(sorted(dates.items())) == c['first_observed_transition_year_counts']
histories = pd.DataFrame(history_rows)
display(histories.replace({'occupied': 'occupé', 'vacant': 'vacant', 'missing': 'non observé', 'unknown': 'inconnu'}))| Dossier | Locaux appariés n | Occupés / vacants 2021 | Adresse | Étage | Unité | 2021 | 2022 | 2023 | 2024 | 2025 | Première observation du nouvel état | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Lachine | 25 | 20 / 5 | 685 rue Notre-Dame O | RC | Non fourni | vacant | vacant | occupé | occupé | occupé | 2023-08-28 |
| 1 | Lachine | 25 | 20 / 5 | 675 rue Notre-Dame O | RC | Non fourni | vacant | occupé | occupé | occupé | occupé | 2022-07-28 |
| 2 | Lachine | 25 | 20 / 5 | 845 rue Notre-Dame O | RC | Non fourni | vacant | occupé | occupé | occupé | occupé | 2022-07-28 |
| 3 | Ahuntsic | 44 | 43 / 1 | 9801 boulevard Saint-Laurent | DEMI SOUS-SOL | A | occupé | vacant | occupé | occupé | vacant | 2022-07-11 |
| 4 | Ahuntsic | 44 | 43 / 1 | 10134 rue Lajeunesse | RC | Non fourni | occupé | vacant | vacant | vacant | vacant | 2022-07-11 |
| 5 | Ahuntsic | 44 | 43 / 1 | 10215 boulevard Saint-Laurent | RC | Non fourni | occupé | occupé | vacant | vacant | vacant | 2023-06-27 |
| 6 | Ahuntsic | 44 | 43 / 1 | 10136 rue Lajeunesse | RC | Non fourni | occupé | occupé | occupé | occupé | vacant | 2025-07-03 |
| 7 | Ahuntsic | 44 | 43 / 1 | 9801 boulevard Saint-Laurent | DEMI SOUS-SOL | Non fourni | occupé | occupé | occupé | vacant | vacant | 2024-07-12 |
| 8 | Ahuntsic | 44 | 43 / 1 | 9699 rue Foucher | RC | Non fourni | occupé | occupé | vacant | vacant | vacant | 2023-06-07 |
| 9 | Ahuntsic | 44 | 43 / 1 | 10085 boulevard Saint-Laurent | RC | Non fourni | occupé | occupé | occupé | occupé | vacant | 2025-07-03 |
| 10 | Ahuntsic | 44 | 43 / 1 | 10045 boulevard Saint-Laurent | RC | Non fourni | occupé | occupé | occupé | occupé | vacant | 2025-07-03 |
| 11 | Ahuntsic | 44 | 43 / 1 | 4 rue de Port-Royal E | RC | Non fourni | occupé | vacant | occupé | vacant | vacant | 2022-08-02 |
| 12 | Plateau | 62 | 46 / 16 | 619 avenue Duluth E | RC | Non fourni | vacant | occupé | occupé | occupé | occupé | 2022-05-13 |
| 13 | Plateau | 62 | 46 / 16 | 4159 rue Saint-Denis | RC | Non fourni | vacant | vacant | vacant | occupé | occupé | 2024-05-27 |
| 14 | Plateau | 62 | 46 / 16 | 4123 rue Saint-Denis | SOUS-SOL | Non fourni | vacant | vacant | vacant | vacant | occupé | 2025-06-11 |
| 15 | Plateau | 62 | 46 / 16 | 4117 rue Saint-Denis | RC | Non fourni | vacant | vacant | occupé | occupé | occupé | 2023-05-24 |
| 16 | Plateau | 62 | 46 / 16 | 4109 rue Saint-Denis | RC | Non fourni | vacant | vacant | vacant | vacant | occupé | 2025-06-11 |
| 17 | Plateau | 62 | 46 / 16 | 4097 rue Saint-Denis | RC | Non fourni | vacant | vacant | occupé | occupé | occupé | 2023-05-24 |
| 18 | Plateau | 62 | 46 / 16 | 4065 rue Saint-Denis | RC | Non fourni | vacant | vacant | vacant | occupé | occupé | 2024-05-27 |
| 19 | Plateau | 62 | 46 / 16 | 411 avenue Duluth E | RC | Non fourni | vacant | occupé | occupé | occupé | occupé | 2022-05-13 |
| 20 | Plateau | 62 | 46 / 16 | 752 rue Rachel E | RC | Non fourni | vacant | vacant | vacant | occupé | occupé | 2024-05-23 |
| 21 | Plateau | 62 | 46 / 16 | 800 rue Rachel E | RC | Non fourni | vacant | occupé | occupé | occupé | occupé | 2022-05-13 |
| 22 | Plateau | 62 | 46 / 16 | 964 rue Rachel E | RC | Non fourni | vacant | vacant | vacant | occupé | occupé | 2024-05-23 |
| 23 | Plateau | 62 | 46 / 16 | 980 rue Rachel E | RC | Non fourni | vacant | occupé | occupé | occupé | occupé | 2022-05-13 |
Replacer les cas dans le contexte montréalais
L’exploration achevée retient 267 secteurs strictement admissibles. L’association de Spearman déjà consignée entre croissance antérieure et variation ultérieure de la part occupée est d’environ −0,028. Elle est reprise du dossier, sans nouvelle estimation. Une association de rang proche de zéro n’exclut pas des relations locales ou non linéaires et ne prouve pas une causalité.
Afficher le code Python · cellule 11
relationship = study['relationship']
assert relationship['n'] == manifest['eligible_scatter_n'] == 267
print(f"Association déjà consignée : Spearman rho={relationship['spearman']:.3f}; n={relationship['n']}.")
display(Image(filename=str(ASSETS / 'figures/population_vs_commerce.png'), width=1000))
display(Image(filename=str(ASSETS / 'figures/three_case_comparison.png'), width=1000))Association déjà consignée : Spearman rho=-0.028; n=267.
Figures comparatives originales. Population 2016–2021 ; locaux commerciaux 2021–2025. Le nuage conserve l’étendue complète ; trois observations sont hors de la vue de détail. Ensembles initiaux : Lachine 20 occupés/5 vacants, n=25 ; Ahuntsic 43/1, n=44 ; Plateau 46/16, n=62. Les transitions tracées à gauche demeurent des décomptes non négatifs. Les cas illustrent des contrastes, sans classement. Sources : Statistique Canada ; Ville de Montréal ; étude TERRIQ.
Verdun : ce que le nombre cache
Le total codé est exact, mais les descriptions distinguent construction résidentielle principale et autres travaux. La méthode d’enquête modifie également la lecture d’une baisse d’inventaire. Ces corrections sont déjà établies dans l’étude.
Afficher le code Python · cellule 12
v = study['verdun']
assert v['housing_CO'] == v['principal_housing_descriptions'] + v['excavation_descriptions'] + v['other_descriptions']
permit_review = pd.DataFrame([
['Événements codés construction résidentielle', v['housing_CO']],
['Descriptions directes de construction principale', v['principal_housing_descriptions']],
['Excavations associées', v['excavation_descriptions']],
['Autres descriptions de travaux', v['other_descriptions']],
], columns=['Description, 2016–2020', 'Événements'])
display(permit_review)
display(pd.DataFrame(v['galt_records'])[['issued_date', 'nature_travaux', 'raw_dwellings_value']]
.rename(columns={'issued_date': 'Date d’autorisation', 'nature_travaux': 'Description originale au 200 Galt', 'raw_dwellings_value': 'Champ des logements touchés'}))
verdun_inventory = pd.DataFrame([
['Tous les étages principaux observés', *v['all_floor_stock']],
['Deuxièmes étages exclus aux deux dates', *v['comparable_stock']],
], columns=['Périmètre de l’enquête', 'Locaux 2021', 'Locaux 2025'])
verdun_inventory['Variation de l’inventaire observé'] = verdun_inventory['Locaux 2025'] - verdun_inventory['Locaux 2021']
assert v['all_floor_stock'][0] - v['comparable_stock'][0] == v['upper_floor_cohort'] == 18
display(verdun_inventory)| Description, 2016–2020 | Événements | |
|---|---|---|
| 0 | Événements codés construction résidentielle | 289 |
| 1 | Descriptions directes de construction principale | 2 |
| 2 | Excavations associées | 2 |
| 3 | Autres descriptions de travaux | 285 |
| Date d’autorisation | Description originale au 200 Galt | Champ des logements touchés | |
|---|---|---|---|
| 0 | 2016-08-31 | EXT - Réfection de la façade et du mur arrière en brique (même brique réutilisée). | 37 |
| 1 | 2016-10-03 | EXT - Abattage d'un tilleul situé dans la cour avant car mort. | 37 |
| Périmètre de l’enquête | Locaux 2021 | Locaux 2025 | Variation de l’inventaire observé | |
|---|---|---|---|---|
| 0 | Tous les étages principaux observés | 104 | 90 | -14 |
| 1 | Deuxièmes étages exclus aux deux dates | 86 | 90 | 4 |
Observation. Les 289 événements codés comprennent 2 descriptions directes de construction résidentielle principale, 2 excavations associées et 285 autres descriptions. Au 200, rue Galt, les 37 logements touchés se répètent pour réparation de briques et retrait d’arbre mort : ce ne sont pas 74 logements neufs. Les 18 fiches occupées d’étages supérieurs vues en 2021 et 2022 sont absentes en 2023, au changement de périmètre.
Interprétation. Les agrégats ne démontrent pas des centaines de nouveaux projets résidentiels et une disparition physique des locaux.
Question ouverte. Logements achevés et statut physique des locaux non rapprochés. Des fiches manquantes ne certifient pas des fermetures.
Limites de lecture
- Deux recensements ne décrivent pas les déplacements annuels de population.
- Les intervalles démographique et commercial sont distincts ; ordre des événements et coexistence ne prouvent pas une causalité.
- Dates de permis et années foncières ne certifient pas un achèvement. Le rôle est un état de 2026.
- Les secteurs ne représentent ni tout le quartier ni une zone de clientèle.
- Le panel sélectionne des locaux identifiables ; l’inventaire comporte aussi des locaux non appariés.
- Le périmètre constant d’étages réduit une différence de couverture connue.
- L’occupation ne mesure pas ouvertures, fermetures, revenus, profit, emploi ou survie d’entreprise.
- Les cas illustrent des contrastes. Loyers, demande, interventions et transports historiques ne sont pas établis comme causes.
Afficher le code Python · cellule 13
from render_latex_report_fr import SOURCES
source_rows = [{'Source': author, 'Usage': description.replace('--', '–'), 'URL publique originale': url}
for author, description, url in SOURCES]
display(pd.DataFrame(source_rows))
for source in study['methodology_sources']['files']:
display(Markdown(f"**{source['edition']} méthode de l’enquête :** [PDF public original]({source['url']}); "
f"[copie vérifiée](../prototype/assets/methodology/{source['path']})."))| Source | Usage | URL publique originale | |
|---|---|---|---|
| 0 | Statistique Canada | Recensement de 2021, comparateur préparé de 2016 et géographie des secteurs de 2021. | https://www12.statcan.gc.ca/census-recensement/2021/dp-pd/prof/index.cfm?Lang=E |
| 1 | Ville de Montréal | Permis de construction : autorisations, dates, codes et descriptions originales. | https://www.donneesquebec.ca/recherche/dataset/vmtl-permis-construction |
| 2 | Ville de Montréal | Locaux commerciaux : éditions de 2021 à 2025 et documents de méthode. | https://www.donneesquebec.ca/recherche/dataset/vmtl-locaux-commerciaux |
| 3 | Ville de Montréal | Unités d’évaluation foncière : état modifié le 23 septembre 2026 et consulté le 30 septembre 2026. | https://www.donneesquebec.ca/recherche/dataset/vmtl-unites-evaluation-fonciere |
| 4 | Ville de Montréal | Limites administratives actuelles, à titre de contexte cartographique. | https://donneesquebec.ca/recherche/dataset/vmtl-limites-administratives-agglomeration |
| 5 | SCHL | Contexte résidentiel déjà préparé ; limites de géographie et de calendrier. | https://www03.cmhc-schl.gc.ca/hmip-pimh/en |
2025 méthode de l’enquête : PDF public original; copie vérifiée.
2023 méthode de l’enquête : PDF public original; copie vérifiée.
2022 méthode de l’enquête : PDF public original; copie vérifiée.
Attributions
Adapté de Statistique Canada, Recensement de la population de 2021, comparateur préparé de 2016 et secteurs de 2021. Cette adaptation ne constitue pas une approbation du produit par Statistique Canada. Licence ouverte de Statistique Canada. Les données municipales et les figures sont adaptées de la Ville de Montréal sous CC BY 4.0. TERRIQ est responsable des adaptations et interprétations. Le contexte complémentaire reprend les données de la SCHL déjà préparées ; aucune approbation municipale, de Statistique Canada ou de la SCHL n’est suggérée.
Conclusion
À Lachine, logement et croissance antérieure coexistent avec une réoccupation localisée. Ahuntsic associe croissance antérieure et vacance accrue. Le Plateau présente une réoccupation substantielle avec peu de croissance antérieure. Définitions, dates et géographie changent la lecture des indicateurs.
Générer le rapport LaTeX français
La dernière cellule écrit le document autonome à partir des preuves vérifiées. Ses barres natives reprennent les mêmes dénominateurs. Les cartes et figures originales sont conservées dans le notebook. Les entrées analytiques et les documents d’origine sont lus sans modification.
Afficher le code Python · cellule 14
from render_latex_report_fr import write_report
report_path = write_report(study, DELIVERABLES / 'montreal_neighbourhood_change_fr.tex')
print(f'Création de {report_path.name} à partir des preuves vérifiées.')
print('Vérifications terminées. Le notebook reprend l’étude achevée.')Création de montreal_neighbourhood_change_fr.tex à partir des preuves vérifiées. Vérifications terminées. Le notebook reprend l’étude achevée.