MapleStatsMCP

Études de cas

Huit métiers, une seule connexion

Que pouvez-vous faire avec MapleStats ? Huit questions de huit métiers différents, chacune résolue par des appels aux outils de MapleStats. Voyez par vous-même ci-dessous (petit avertissement : c'est impressionnant).

158

fichiers de microdonnées pour les statisticiens, gardiens des poids et des marges,

Le baccalauréat, province par province

La part des adultes de 25 à 64 ans dont le plus haut diplôme est un baccalauréat, estimée à partir du fichier de microdonnées à grande diffusion du recensement de 2021 avec ses poids d'enquête. Chaque barre est un intervalle de confiance à 95 % tiré des 16 poids de réplication du fichier : le plus étroit pour la Colombie-Britannique et le Québec, le plus large pour le Nord canadien et l'Île-du-Prince-Édouard, où l'échantillon est plus petit.

Part des adultes de 25 à 64 ans dont le plus haut diplôme est un baccalauréat, avec intervalles de confiance à 95 %10,0 %15,0 %20,0 %25,0 %Terre-Neuve-et-Labrador : 13,3 % (12,5 % à 14,1 %)Terre-Neuve-et-Labrador13,3 %Île-du-Prince-Édouard : 18,2 % (16,2 % à 20,1 %)Île-du-Prince-Édouard18,2 %Nouvelle-Écosse : 20,1 % (19,4 % à 20,7 %)Nouvelle-Écosse20,1 %Nouveau-Brunswick : 16,7 % (16,0 % à 17,3 %)Nouveau-Brunswick16,7 %Québec : 18,1 % (17,9 % à 18,4 %)Québec18,1 %Ontario : 23,8 % (23,6 % à 24,1 %)Ontario23,8 %Manitoba : 20,3 % (19,8 % à 20,9 %)Manitoba20,3 %Saskatchewan : 18,5 % (17,8 % à 19,2 %)Saskatchewan18,5 %Alberta : 21,7 % (21,3 % à 22,1 %)Alberta21,7 %Colombie-Britannique : 22,9 % (22,7 % à 23,1 %)Colombie-Britannique22,9 %Nord canadien : 15,1 % (13,7 % à 16,4 %)Nord canadien15,1 %Part des adultes de 25 à 64 ans dont le plus haut diplôme est un baccalauréat, avec intervalles de confiance à 95 %10,0 %15,0 %20,0 %25,0 %Terre-Neuve-et-Labrador : 13,3 % (12,5 % à 14,1 %)Terre-Neuve-et-Labrador13,3 %Île-du-Prince-Édouard : 18,2 % (16,2 % à 20,1 %)Île-du-Prince-Édouard18,2 %Nouvelle-Écosse : 20,1 % (19,4 % à 20,7 %)Nouvelle-Écosse20,1 %Nouveau-Brunswick : 16,7 % (16,0 % à 17,3 %)Nouveau-Brunswick16,7 %Québec : 18,1 % (17,9 % à 18,4 %)Québec18,1 %Ontario : 23,8 % (23,6 % à 24,1 %)Ontario23,8 %Manitoba : 20,3 % (19,8 % à 20,9 %)Manitoba20,3 %Saskatchewan : 18,5 % (17,8 % à 19,2 %)Saskatchewan18,5 %Alberta : 21,7 % (21,3 % à 22,1 %)Alberta21,7 %Colombie-Britannique : 22,9 % (22,7 % à 23,1 %)Colombie-Britannique22,9 %Nord canadien : 15,1 % (13,7 % à 16,4 %)Nord canadien15,1 %
Voir les données
Part des adultes de 25 à 64 ans dont le plus haut diplôme est un baccalauréat, avec intervalles de confiance à 95 %
ProvinceEstimationIC à 95 %, borne inf.IC à 95 %, borne sup.
Terre-Neuve-et-Labrador13,3 %12,5 %14,1 %
Île-du-Prince-Édouard18,2 %16,2 %20,1 %
Nouvelle-Écosse20,1 %19,4 %20,7 %
Nouveau-Brunswick16,7 %16,0 %17,3 %
Québec18,1 %17,9 %18,4 %
Ontario23,8 %23,6 %24,1 %
Manitoba20,3 %19,8 %20,9 %
Saskatchewan18,5 %17,8 %19,2 %
Alberta21,7 %21,3 %22,1 %
Colombie-Britannique22,9 %22,7 %23,1 %
Nord canadien15,1 %13,7 %16,4 %
Source : Statistique Canada, Recensement de 2021, fichier des particuliers (98M0001X), interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "statcan_pumf_tabulate", "arguments": {"url": "https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip", "rows": ["PR", "HDGREE"], "statistic": "share", "filters": {"AGEGRP": ["9", "10", "11", "12", "13", "14", "15", "16"], "HDGREE": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"]}}}

R

# ============================================================
# StatCan public use microdata file
# Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
#          (exact: the PUMF ZIP)
# Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
# Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
# ============================================================

# 0. Setup ----

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

download.file("https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip", "data/raw/cen21_ind_98m0001x_part_rec21.zip", mode = "wb")
zip::unzip("data/raw/cen21_ind_98m0001x_part_rec21.zip", exdir = "data/raw/cen21_ind_98m0001x_part_rec21")
file.remove("data/raw/cen21_ind_98m0001x_part_rec21.zip")

data_files <- list.files("data/raw/cen21_ind_98m0001x_part_rec21", full.names = TRUE, recursive = TRUE)

Python

# ============================================================
# StatCan public use microdata file
# Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
#          (exact: the PUMF ZIP)
# Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
# Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
# ============================================================

# %% 0. Setup

import zipfile
from pathlib import Path

import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "cen21_ind_98m0001x_part_rec21.zip"

# %% 1. Read inputs

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=300,
    headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"},
) as client:
    response = client.get('https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip')
response.raise_for_status()
raw_path.write_bytes(response.content)

with zipfile.ZipFile(raw_path) as archive:
    archive.extractall(RAW_DIR / raw_path.stem)

Stata

* ============================================================
* StatCan public use microdata file
* Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
*          (exact: the PUMF ZIP)
* Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
* Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/statcan_pumf_tabulate.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
import zipfile
from pathlib import Path
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "cen21_ind_98m0001x_part_rec21.zip"
with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.get('https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip')
response.raise_for_status()
raw_path.write_bytes(response.content)
with zipfile.ZipFile(raw_path) as archive: archive.extractall(RAW_DIR / raw_path.stem)
end

log close

Julia

# ============================================================
# StatCan public use microdata file
# Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
#          (exact: the PUMF ZIP)
# Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
# Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
# ============================================================

# 0. Setup

using Downloads

mkpath("data/raw")

# 1. Read inputs

Downloads.download("https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip", "data/raw/cen21_ind_98m0001x_part_rec21.zip")

67

tableaux d'immigration pour les démographes, qui comptent ceux qui viennent s'établir,

Ceux qui viennent s'établir : Edmonton et Calgary

Les nouveaux résidents permanents qui ont indiqué chaque ville comme destination, par année, d'après les mises à jour mensuelles d'IRCC. *2026 ne couvre que les mois de janvier à juillet. IRCC arrondit chaque compte à un multiple de 5.

Nouveaux résidents permanents selon la destination prévue, Edmonton et Calgary, par annéeEdmontonCalgary010 00020 00030 00040 000Edmonton, 2015 : 16 745Calgary, 2015 : 21 7152015Edmonton, 2016 : 17 895Calgary, 2016 : 21 4302016Edmonton, 2017 : 15 960Calgary, 2017 : 17 8752017Edmonton, 2018 : 15 750Calgary, 2018 : 18 9502018Edmonton, 2019 : 16 425Calgary, 2019 : 19 6352019Edmonton, 2020 : 8 380Calgary, 2020 : 10 6652020Edmonton, 2021 : 14 760Calgary, 2021 : 17 8702021Edmonton, 2022 : 17 360Calgary, 2022 : 24 7252022Edmonton, 2023 : 21 665Calgary, 2023 : 27 4302023Edmonton, 2024 : 24 075Calgary, 2024 : 31 1652024Edmonton, 2025 : 18 980Calgary, 2025 : 23 5602025Edmonton, 2026* : 10 685Calgary, 2026* : 13 4002026*Nouveaux résidents permanents selon la destination prévue, Edmonton et Calgary, par annéeEdmontonCalgary010 00020 00030 00040 000Edmonton, 2015 : 16 745Calgary, 2015 : 21 7152015Edmonton, 2016 : 17 895Calgary, 2016 : 21 4302016Edmonton, 2017 : 15 960Calgary, 2017 : 17 8752017Edmonton, 2018 : 15 750Calgary, 2018 : 18 9502018Edmonton, 2019 : 16 425Calgary, 2019 : 19 6352019Edmonton, 2020 : 8 380Calgary, 2020 : 10 6652020Edmonton, 2021 : 14 760Calgary, 2021 : 17 8702021Edmonton, 2022 : 17 360Calgary, 2022 : 24 7252022Edmonton, 2023 : 21 665Calgary, 2023 : 27 4302023Edmonton, 2024 : 24 075Calgary, 2024 : 31 1652024Edmonton, 2025 : 18 980Calgary, 2025 : 23 5602025Edmonton, 2026* : 10 685Calgary, 2026* : 13 4002026*
Voir les données
Nouveaux résidents permanents selon la destination prévue, Edmonton et Calgary, par année
AnnéeEdmontonCalgary
201516 74521 715
201617 89521 430
201715 96017 875
201815 75018 950
201916 42519 635
20208 38010 665
202114 76017 870
202217 36024 725
202321 66527 430
202424 07531 165
202518 98023 560
2026*10 68513 400
Source : https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv, interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "ircc_monthly_query", "arguments": {"table_id": "ODP-PR-PT_CMA", "filters": {"census_metropolitan_area": "Edmonton"}, "period": "year", "year_from": 2015}}
Requêtecall_tool
{"name": "ircc_monthly_query", "arguments": {"table_id": "ODP-PR-PT_CMA", "filters": {"census_metropolitan_area": "Calgary"}, "period": "year", "year_from": 2015}}

R

# ============================================================
# IRCC monthly update: Canada - Permanent Residents by Province/Territory and CMA
# Purpose: Fetch the data behind MapleStats MCP's ircc_monthly_query
#          (exact: the source file, then the tool's filters)
# Inputs:  https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv
# Outputs: data/raw/ODP-PR-PT_CMA.csv; the prepared table as `data`
# ============================================================

# 0. Setup ----

library(dplyr)
library(janitor)
library(readr)
library(stringr)

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

download.file("https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv", "data/raw/ODP-PR-PT_CMA.csv", mode = "wb")

data <- read_delim("data/raw/ODP-PR-PT_CMA.csv", delim = "\t", na = c("", "NA", "--"), show_col_types = FALSE)

# 2. Check inputs ----

stopifnot("https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv returned no rows" = nrow(data) > 0)

# 3. Prepare data ----

# Keep the rows the MapleStats tool kept.

data <- data |>
  filter(
    str_to_lower(str_trim(`EN_CENSUS_METROPOLITAN_AREA`)) == "edmonton",
    `EN_YEAR` >= 2015
  )

data <- data |>
  clean_names()

# Standard cleaning: trimmed text, empty strings as missing, and numbers
# stored as text converted to numbers.

data <- data |>
  mutate(across(where(is.character), \(x) na_if(str_trim(x), ""))) |>
  type_convert()

Python

# ============================================================
# IRCC monthly update: Canada - Permanent Residents by Province/Territory and CMA
# Purpose: Fetch the data behind MapleStats MCP's ircc_monthly_query
#          (exact: the source file, then the tool's filters)
# Inputs:  https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv
# Outputs: data/raw/ODP-PR-PT_CMA.csv; the prepared table as `data`
# ============================================================

# %% 0. Setup

import re
import unicodedata
from pathlib import Path

import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "ODP-PR-PT_CMA.csv"

# %% 1. Read inputs

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=300,
    headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"},
) as client:
    response = client.get('https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv')
response.raise_for_status()
raw_path.write_bytes(response.content)

data = pl.read_csv(raw_path, separator='\t', infer_schema_length=100_000, null_values=['--'])

# %% 2. Check inputs

assert data.height > 0, "https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv returned no rows"

# %% 3. Prepare data

# Keep the rows the MapleStats tool kept.

data = data.filter(
    pl.col('EN_CENSUS_METROPOLITAN_AREA').cast(pl.Utf8).str.strip_chars().str.to_lowercase() == 'edmonton',
    pl.col('EN_YEAR').cast(pl.Float64, strict=False) >= 2015,
)

# Standard cleaning: snake_case names without accents (PÉRIODE -> periode,
# referenceNumber -> reference_number, as janitor does in R), trimmed text,
# empty strings as missing.

data = data.rename(
    {
        column: re.sub(
            r"[^0-9a-z]+",
            "_",
            re.sub(
                r"([a-z0-9])([A-Z])",
                r"\1_\2",
                unicodedata.normalize("NFKD", column).encode("ascii", "ignore").decode(),
            ).lower(),
        ).strip("_")
        for column in data.columns
    }
)
data = data.with_columns(pl.col(pl.Utf8).str.strip_chars().replace("", None))

Stata

* ============================================================
* IRCC monthly update: Canada - Permanent Residents by Province/Territory and CMA
* Purpose: Fetch the data behind MapleStats MCP's ircc_monthly_query
*          (exact: the source file, then the tool's filters)
* Inputs:  https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv
* Outputs: data/raw/ODP-PR-PT_CMA.csv; the prepared table as `data`
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/ircc_monthly_query.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
from pathlib import Path
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "ODP-PR-PT_CMA.csv"
with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.get('https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv')
response.raise_for_status()
raw_path.write_bytes(response.content)
data = pl.read_csv(raw_path, separator='\t', infer_schema_length=100_000, null_values=['--'])
data = data.filter(pl.col('EN_CENSUS_METROPOLITAN_AREA').cast(pl.Utf8).str.strip_chars().str.to_lowercase() == 'edmonton', pl.col('EN_YEAR').cast(pl.Float64, strict=False) >= 2015)
nested = [name for name, dtype in data.schema.items() if dtype.is_nested()]
data = data.with_columns(pl.col(name).map_elements(lambda value: json.dumps(value.to_list() if isinstance(value, pl.Series) else value, default=str), return_dtype=pl.Utf8) for name in nested)
data.write_csv(RAW_DIR / "ODP-PR-PT_CMA_prepared.csv")
end

import delimited "data/raw/ODP-PR-PT_CMA_prepared.csv", clear varnames(1) encoding("utf-8")

* 2. Check inputs

assert _N > 0

* 3. Prepare data

* Standard cleaning: lower-case names, trimmed text, and numbers stored as
* text converted (destring leaves genuinely non-numeric text alone).

rename *, lower
quietly ds, has(type string)
local text_vars `r(varlist)'
foreach var of local text_vars {
    replace `var' = strtrim(`var')
}
destring, replace

log close

Julia

# ============================================================
# IRCC monthly update: Canada - Permanent Residents by Province/Territory and CMA
# Purpose: Fetch the data behind MapleStats MCP's ircc_monthly_query
#          (exact: the source file, then the tool's filters)
# Inputs:  https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv
# Outputs: data/raw/ODP-PR-PT_CMA.csv; the prepared table as `data`
# ============================================================

# 0. Setup

using DataFrames
using Downloads
using TidierData
using TidierFiles

mkpath("data/raw")

# 1. Read inputs

Downloads.download("https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv", "data/raw/ODP-PR-PT_CMA.csv")
data = read_csv("data/raw/ODP-PR-PT_CMA.csv", delim = "\t", missingstring = ["", "--"])

# 2. Check inputs

@assert nrow(data) > 0 "https://www.ircc.canada.ca/opendata-donneesouvertes/data/ODP-PR-PT_CMA.csv returned no rows"

# 3. Prepare data

# Keep the rows the MapleStats tool kept.

data = filter(row -> begin
        (lowercase(strip(coalesce(string(row["EN_CENSUS_METROPOLITAN_AREA"]), ""))) == "edmonton") &&
        (!ismissing(row["EN_YEAR"]) && row["EN_YEAR"] >= 2015)
    end, data)

# Standard cleaning: snake_case names, trimmed text, empty strings as missing.

data = @chain data begin
    @clean_names
end
data = mapcols(
    col -> eltype(col) <: Union{Missing, AbstractString} ?
        [ismissing(x) || isempty(strip(x)) ? missing : strip(x) for x in col] : col,
    data,
)

54

tableaux sur le logement pour les urbanistes, qui comptent les grues avant les clés,

Le Canada construit en hauteur, pas en largeur

Les mises en chantier par année selon la SCHL, pour les maisons individuelles et les appartements, dans les centres de 10 000 habitants ou plus. En 2005, le Canada a mis en chantier 93 994 maisons individuelles et 66 404 appartements ; en 2025, 41 837 et 164 839. Les appartements sont passés de 34 % de toutes les mises en chantier à 68 %, et dépassent les maisons individuelles chaque année depuis 2011.

Mises en chantier au Canada par année, maisons individuelles et appartements, centres de 10 000 habitants ou plusIndividuellesAppartements050 000100 000150 000200 000Individuelles, 2005 : 93 994Appartements, 2005 : 66 4042005Individuelles, 2006 : 94 110Appartements, 2006 : 69 1502006Individuelles, 2007 : 90 855Appartements, 2007 : 69 0622007Individuelles, 2008 : 74 435Appartements, 2008 : 83 5652008Individuelles, 2009 : 60 521Appartements, 2009 : 46 3882009Individuelles, 2010 : 74 244Appartements, 2010 : 61 3962010Individuelles, 2011 : 67 089Appartements, 2011 : 77 1192011Individuelles, 2012 : 67 172Appartements, 2012 : 92 9512012Individuelles, 2013 : 63 143Appartements, 2013 : 76 0092013Individuelles, 2014 : 62 380Appartements, 2014 : 76 5992014Individuelles, 2015 : 57 739Appartements, 2015 : 92 5642015Individuelles, 2016 : 60 549Appartements, 2016 : 88 0072016Individuelles, 2017 : 63 495Appartements, 2017 : 100 3652017Individuelles, 2018 : 54 180Appartements, 2018 : 109 8202018Individuelles, 2019 : 46 909Appartements, 2019 : 115 6642019Individuelles, 2020 : 49 704Appartements, 2020 : 119 4282020Individuelles, 2021 : 63 456Appartements, 2021 : 141 6842021Individuelles, 2022 : 57 515Appartements, 2022 : 144 0432022Individuelles, 2023 : 42 924Appartements, 2023 : 147 6172023Individuelles, 2024 : 44 357Appartements, 2024 : 149 1132024Individuelles, 2025 : 41 837Appartements, 2025 : 164 8392025Mises en chantier au Canada par année, maisons individuelles et appartements, centres de 10 000 habitants ou plusIndividuellesAppartements050 000100 000150 000200 000Individuelles, 2005 : 93 994Appartements, 2005 : 66 4042005Individuelles, 2006 : 94 110Appartements, 2006 : 69 1502006Individuelles, 2007 : 90 855Appartements, 2007 : 69 0622007Individuelles, 2008 : 74 435Appartements, 2008 : 83 5652008Individuelles, 2009 : 60 521Appartements, 2009 : 46 3882009Individuelles, 2010 : 74 244Appartements, 2010 : 61 3962010Individuelles, 2011 : 67 089Appartements, 2011 : 77 1192011Individuelles, 2012 : 67 172Appartements, 2012 : 92 9512012Individuelles, 2013 : 63 143Appartements, 2013 : 76 0092013Individuelles, 2014 : 62 380Appartements, 2014 : 76 5992014Individuelles, 2015 : 57 739Appartements, 2015 : 92 5642015Individuelles, 2016 : 60 549Appartements, 2016 : 88 0072016Individuelles, 2017 : 63 495Appartements, 2017 : 100 3652017Individuelles, 2018 : 54 180Appartements, 2018 : 109 8202018Individuelles, 2019 : 46 909Appartements, 2019 : 115 6642019Individuelles, 2020 : 49 704Appartements, 2020 : 119 4282020Individuelles, 2021 : 63 456Appartements, 2021 : 141 6842021Individuelles, 2022 : 57 515Appartements, 2022 : 144 0432022Individuelles, 2023 : 42 924Appartements, 2023 : 147 6172023Individuelles, 2024 : 44 357Appartements, 2024 : 149 1132024Individuelles, 2025 : 41 837Appartements, 2025 : 164 8392025
Voir les données
Mises en chantier au Canada par année, maisons individuelles et appartements, centres de 10 000 habitants ou plus
AnnéeIndividuellesAppartements
200593 99466 404
200694 11069 150
200790 85569 062
200874 43583 565
200960 52146 388
201074 24461 396
201167 08977 119
201267 17292 951
201363 14376 009
201462 38076 599
201557 73992 564
201660 54988 007
201763 495100 365
201854 180109 820
201946 909115 664
202049 704119 428
202163 456141 684
202257 515144 043
202342 924147 617
202444 357149 113
202541 837164 839
Source : https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable, interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "cmhc_get_table_data", "arguments": {"category_level_1": "New Housing Construction", "category_level_2": "Starts (Actual)", "column_field": "1", "row_field": "TIMESERIES"}}
Requêtecall_tool
{"name": "cmhc_list_categories", "arguments": {}}

R

# ============================================================
# Reproduce cmhc_get_table_data
# Purpose: Fetch the data behind MapleStats MCP's cmhc_get_table_data
#          (exact: the request the tool made, recorded while it ran)
# Inputs:  https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable
# Outputs: data/raw/ExportTable.txt; the downloaded file
# ============================================================

# 0. Setup ----

library(httr2)

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

request("https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable") |>
  req_body_form(!!!list(`TableId` = "5.6.1", `GeographyId` = "1", `GeographyTypeId` = "1", `exportType` = "csv")) |>
  req_perform(path = "data/raw/ExportTable.txt")

Python

# ============================================================
# Reproduce cmhc_get_table_data
# Purpose: Fetch the data behind MapleStats MCP's cmhc_get_table_data
#          (exact: the request the tool made, recorded while it ran)
# Inputs:  https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable
# Outputs: data/raw/ExportTable.txt; the downloaded file
# ============================================================

# %% 0. Setup

from pathlib import Path

import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "ExportTable.txt"

# %% 1. Read inputs

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=300,
    headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"},
) as client:
    response = client.post('https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable', data={'TableId': '5.6.1', 'GeographyId': '1', 'GeographyTypeId': '1', 'exportType': 'csv'})
response.raise_for_status()
raw_path.write_bytes(response.content)

Stata

* ============================================================
* Reproduce cmhc_get_table_data
* Purpose: Fetch the data behind MapleStats MCP's cmhc_get_table_data
*          (exact: the request the tool made, recorded while it ran)
* Inputs:  https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable
* Outputs: data/raw/ExportTable.txt; the downloaded file
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/cmhc_get_table_data.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
from pathlib import Path
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "ExportTable.txt"
with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.post('https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable', data={'TableId': '5.6.1', 'GeographyId': '1', 'GeographyTypeId': '1', 'exportType': 'csv'})
response.raise_for_status()
raw_path.write_bytes(response.content)
end

log close

Julia

# ============================================================
# Reproduce cmhc_get_table_data
# Purpose: Fetch the data behind MapleStats MCP's cmhc_get_table_data
#          (exact: the request the tool made, recorded while it ran)
# Inputs:  https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable
# Outputs: data/raw/ExportTable.txt; the downloaded file
# ============================================================

# 0. Setup

using Downloads

mkpath("data/raw")

# 1. Read inputs

Downloads.request(
    "https://www03.cmhc-schl.gc.ca/hmip-pimh/en/TableMapChart/ExportTable";
    headers = ["Content-Type" => "application/x-www-form-urlencoded"], method = "POST", input = IOBuffer("TableId=5.6.1&GeographyId=1&GeographyTypeId=1&exportType=csv"),
    output = "data/raw/ExportTable.txt",
)

962 449

fiches du recensement pour les microéconomistes, tisseurs d'incitations, une vie à la fois,

Le faible revenu d'une génération à l'autre

La part des personnes sous la mesure de faible revenu après impôt, selon la génération, tirée des mêmes microdonnées du recensement de 2021, avec des intervalles de confiance à 95 % issus des poids de réplication. Les immigrants de première génération sont les plus susceptibles d'être à faible revenu (14,0 %). Leurs enfants, nés au Canada, le sont le moins (9,1 % et 8,8 %), moins que la troisième génération ou plus (10,3 %).

Part des personnes à faible revenu (MFR-ApI) selon la génération, recensement de 2021, avec intervalles de confiance à 95 %8,0 %10,0 %12,0 %14,0 %16,0 %Première génération (née à l'étranger) : 14,0 % (13,8 % à 14,1 %)Première génération (née àl'étranger)14,0 %Deuxième génération, deux parents nés à l'étranger : 9,1 % (9,0 % à 9,3 %)Deuxième génération, deuxparents nés à l'étranger9,1 %Deuxième génération, un parent né à l'étranger : 8,8 % (8,6 % à 9,1 %)Deuxième génération, unparent né à l'étranger8,8 %Troisième génération ou plus : 10,3 % (10,2 % à 10,4 %)Troisième génération ou plus10,3 %Part des personnes à faible revenu (MFR-ApI) selon la génération, recensement de 2021, avec intervalles de confiance à 95 %8,0 %12,0 %16,0 %Première génération (née à l'étranger) : 14,0 % (13,8 % à 14,1 %)Première génération (née àl'étranger)14,0 %Deuxième génération, deux parents nés à l'étranger : 9,1 % (9,0 % à 9,3 %)Deuxième génération, deuxparents nés à l'étranger9,1 %Deuxième génération, un parent né à l'étranger : 8,8 % (8,6 % à 9,1 %)Deuxième génération, unparent né à l'étranger8,8 %Troisième génération ou plus : 10,3 % (10,2 % à 10,4 %)Troisième génération ou plus10,3 %
Voir les données
Part des personnes à faible revenu (MFR-ApI) selon la génération, recensement de 2021, avec intervalles de confiance à 95 %
GénérationEstimationIC à 95 %, borne inf.IC à 95 %, borne sup.
Première génération (née à l'étranger)14,0 %13,8 %14,1 %
Deuxième génération, deux parents nés à l'étranger9,1 %9,0 %9,3 %
Deuxième génération, un parent né à l'étranger8,8 %8,6 %9,1 %
Troisième génération ou plus10,3 %10,2 %10,4 %
Source : Statistique Canada, Recensement de 2021, fichier des particuliers (98M0001X), interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "statcan_pumf_tabulate", "arguments": {"url": "https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip", "rows": ["GENSTAT", "LOLIMA"], "statistic": "share", "filters": {"GENSTAT": ["1", "2", "3", "4"], "LOLIMA": ["1", "2"]}}}

R

# ============================================================
# StatCan public use microdata file
# Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
#          (exact: the PUMF ZIP)
# Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
# Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
# ============================================================

# 0. Setup ----

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

download.file("https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip", "data/raw/cen21_ind_98m0001x_part_rec21.zip", mode = "wb")
zip::unzip("data/raw/cen21_ind_98m0001x_part_rec21.zip", exdir = "data/raw/cen21_ind_98m0001x_part_rec21")
file.remove("data/raw/cen21_ind_98m0001x_part_rec21.zip")

data_files <- list.files("data/raw/cen21_ind_98m0001x_part_rec21", full.names = TRUE, recursive = TRUE)

Python

# ============================================================
# StatCan public use microdata file
# Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
#          (exact: the PUMF ZIP)
# Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
# Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
# ============================================================

# %% 0. Setup

import zipfile
from pathlib import Path

import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "cen21_ind_98m0001x_part_rec21.zip"

# %% 1. Read inputs

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=300,
    headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"},
) as client:
    response = client.get('https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip')
response.raise_for_status()
raw_path.write_bytes(response.content)

with zipfile.ZipFile(raw_path) as archive:
    archive.extractall(RAW_DIR / raw_path.stem)

Stata

* ============================================================
* StatCan public use microdata file
* Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
*          (exact: the PUMF ZIP)
* Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
* Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/statcan_pumf_tabulate.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
import zipfile
from pathlib import Path
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "cen21_ind_98m0001x_part_rec21.zip"
with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.get('https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip')
response.raise_for_status()
raw_path.write_bytes(response.content)
with zipfile.ZipFile(raw_path) as archive: archive.extractall(RAW_DIR / raw_path.stem)
end

log close

Julia

# ============================================================
# StatCan public use microdata file
# Purpose: Fetch the data behind MapleStats MCP's statcan_pumf_tabulate
#          (exact: the PUMF ZIP)
# Inputs:  https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip
# Outputs: data/raw/cen21_ind_98m0001x_part_rec21.zip; the unzipped files
# ============================================================

# 0. Setup

using Downloads

mkpath("data/raw")

# 1. Read inputs

Downloads.download("https://www150.statcan.gc.ca/n1/pub/98m0001x/2023001/cen21_ind_98m0001x_part_rec21.zip", "data/raw/cen21_ind_98m0001x_part_rec21.zip")

98

cartes de crédit pour les spécialistes du marketing, lecteurs des petits caractères de la concurrence,

Ce que coûte une carte à récompenses

Toutes les cartes répertoriées pour l'Alberta dans l'outil de comparaison des cartes de crédit de l'Agence de la consommation en matière financière du Canada (ACFC), en dollars canadiens et sans les cartes pour étudiants ni les cartes garanties, qui y font l'objet de recherches distinctes : les frais annuels selon le taux d'intérêt sur les achats. Les 82 cartes à récompenses ont des frais médians de 99 $ et un taux médian de 21,99 % ; les 15 cartes sans récompenses, 25 $ et 13,99 %. Les gros points regroupent plusieurs cartes au même prix. Une carte prépayée, sans intérêt, est exclue.

Cartes de crédit répertoriées pour l'Alberta dans l'outil de comparaison de l'ACFC : frais annuels et taux d'intérêt sur les achatsAvec récompensesSans récompensesTaux sur les achats10 %15 %20 %25 %30 %0 $200 $400 $600 $800 $Frais annuels0 $, 21,99 % : Carte RemiseSimple d'American Express, Carte verte American Express, Carte BMO eclipse rise Visa (+12)0 $, 20,99 % : Carte Mastercard Momentum Scotia, Coast Capital Collabria Cash Back Mastercard, moi RBC Visa (+4)139 $, 21,99 % : Carte Mastercard BMO Remises World Elite, Carte Aventura CIBC VISA Infinite, Carte Aventura Or CIBC Visa (+4)120 $, 20,99 % : Coast Capital Collabria Cash Back World Elite Mastercard, Coast Capital Collabria World Mastercard, Avion Visa Infinite RBC (+1)599 $, 21,99 % : Carte Prestige Aéroplan American Express (Carte de crédit), Carte BMO eclipse Visa Infinite Privilege, Carte CIBC Visa Infinite Privilege* Aéroplan (+1)0 $, 20,90 % : Boni Visa Desjardins, Remises Mastercard Desjardins, Remises Visa Desjardins0 $, 21,90 % : Carte RemisesPlus Platine Mastercardᴹᴰ de Capital One®, Carte VoyagesPlus Platine Mastercardᴹᴰ de Capital One®, Carte Récompenses Intelligentes Mastercard de Capital One29 $, 13,99 % : Carte Visa minima Scotia, La carte Mastercard BMO à taux préférentiel, Carte Sélecte CIBC-VISA (taux réduit)89 $, 21,99 % : Carte Mastercard BMO VIPorter, Carte Visa Platine TD Aéroplan, Carte Visa* TD Platine Voyages120 $, 21,99 % : Carte Marriott Bonvoy American Express, Carte BMO eclipse Visa Infinite, Carte Dividendes CIBC VISA Infinite150 $, 21,99 % : Carte Mastercard BMO Ascend World Elite, Carte Mastercard BMO Récompenses Bleu World Elite, Carte Mastercard BMO Récompenses Bleu World Elite (Quebec)0 $, 20,95 % : Carte de crédit avec remises en argent Tangerine, World Mastercard Remises Tangerine0 $, 29,90 % : Carte Mastercard avec garantie à approbation garantie de Capital One, Carte Mastercardᴹᴰ à approbation garantie de Capital One®0 $, 10,90 % : Flexi Visa Desjardins0 $, 12,99 % : Mastercardᴹᴰ La Vraie Ligneᴹᴰ MBNA0 $, 19,99 % : Coast Capital Collabria No Fee Cash Back Business Mastercard0 $, 21,74 % : Mastercard(MD) récompenses Amazon.ca0 $, 21,75 % : Carte CIBC Costco Mastercard0 $, 21,99 % : Classique CIBC Visa20 $, 12,99 % : Visa Classique RBC avec option à taux d'intérêt réduit20 $, 13,99 % : Coast Capital Collabria Classic Mastercard25 $, 12,90 % : Carte Visa TD à faible taux25 $, 12,99 % : Carte de crédit Essentielle American Express25 $, 13,99 % : Coast Capital Collabria Low Rate Business Mastercard30 $, 12,99 % : Servus Personal Low Rate Mastercard39 $, 10,99 % : Mastercardᴹᴰ La Vraie Ligneᴹᴰ Or MBNA39 $, 20,99 % : WestJest Mastercard RBC39 $, 21,99 % : Mastercardᴹᴰ World Argent Content MBNA50 $, 12,99 % : Coast Capital Collabria Centra Gold Mastercard75 $, 20,99 % : Servus Personal Gold Mastercard99 $, 20,99 % : Remise en argent Préférence World Elite Mastercard RBC99 $, 21,99 % : Carte Dividendes Platine CIBC Visa100 $, 20,90 % : Remises World Elite Mastercard Desjardins110 $, 20,90 % : Odyssée Or Visa Desjardins119,88 $, 21,99 % : Carte sélecte RemiseSimple d'American Express120 $, 20,95 % : Mastercard World Elite Récompenses Tangerine120 $, 30,00 % : Carte Aéroplan American Express130 $, 20,90 % : Odyssée World Elite Mastercard Desjardins130 $, 20,99 % : Coast Capital Collabria World Elite Business Mastercard139 $, 20,99 % : WestJet World Elite Mastercard RBC150 $, 20,99 % : Servus Personal World Elite Mastercard165 $, 20,50 % : Visa Infinite British Airways RBC191,88 $, 21,99 % : Carte Cobalt American Express199 $, 21,99 % : Carte Mastercard BMO VIPorter World Elite250 $, 21,99 % : Carte Or avec primes American Express (Carte de crédit)250 $, 30,00 % : Carte Or avec primes American Express (carte de paiement)395 $, 11,90 % : Odyssée Visa Infinite Privilege Desjardins399 $, 20,99 % : Avion Visa Infinite Privilège RBC499 $, 21,99 % : Carte Aventura CIBC Visa Infinite Privilege599 $, 30,00 % : Carte Prestige Aéroplan American Express (Carte de paiement)799 $, 30,00 % : La Carte de PlatineCartes de crédit répertoriées pour l'Alberta dans l'outil de comparaison de l'ACFC : frais annuels et taux d'intérêt sur les achatsAvec récompensesSans récompensesTaux sur les achats10 %15 %20 %25 %30 %0 $200 $400 $600 $800 $Frais annuels0 $, 21,99 % : Carte RemiseSimple d'American Express, Carte verte American Express, Carte BMO eclipse rise Visa (+12)0 $, 20,99 % : Carte Mastercard Momentum Scotia, Coast Capital Collabria Cash Back Mastercard, moi RBC Visa (+4)139 $, 21,99 % : Carte Mastercard BMO Remises World Elite, Carte Aventura CIBC VISA Infinite, Carte Aventura Or CIBC Visa (+4)120 $, 20,99 % : Coast Capital Collabria Cash Back World Elite Mastercard, Coast Capital Collabria World Mastercard, Avion Visa Infinite RBC (+1)599 $, 21,99 % : Carte Prestige Aéroplan American Express (Carte de crédit), Carte BMO eclipse Visa Infinite Privilege, Carte CIBC Visa Infinite Privilege* Aéroplan (+1)0 $, 20,90 % : Boni Visa Desjardins, Remises Mastercard Desjardins, Remises Visa Desjardins0 $, 21,90 % : Carte RemisesPlus Platine Mastercardᴹᴰ de Capital One®, Carte VoyagesPlus Platine Mastercardᴹᴰ de Capital One®, Carte Récompenses Intelligentes Mastercard de Capital One29 $, 13,99 % : Carte Visa minima Scotia, La carte Mastercard BMO à taux préférentiel, Carte Sélecte CIBC-VISA (taux réduit)89 $, 21,99 % : Carte Mastercard BMO VIPorter, Carte Visa Platine TD Aéroplan, Carte Visa* TD Platine Voyages120 $, 21,99 % : Carte Marriott Bonvoy American Express, Carte BMO eclipse Visa Infinite, Carte Dividendes CIBC VISA Infinite150 $, 21,99 % : Carte Mastercard BMO Ascend World Elite, Carte Mastercard BMO Récompenses Bleu World Elite, Carte Mastercard BMO Récompenses Bleu World Elite (Quebec)0 $, 20,95 % : Carte de crédit avec remises en argent Tangerine, World Mastercard Remises Tangerine0 $, 29,90 % : Carte Mastercard avec garantie à approbation garantie de Capital One, Carte Mastercardᴹᴰ à approbation garantie de Capital One®0 $, 10,90 % : Flexi Visa Desjardins0 $, 12,99 % : Mastercardᴹᴰ La Vraie Ligneᴹᴰ MBNA0 $, 19,99 % : Coast Capital Collabria No Fee Cash Back Business Mastercard0 $, 21,74 % : Mastercard(MD) récompenses Amazon.ca0 $, 21,75 % : Carte CIBC Costco Mastercard0 $, 21,99 % : Classique CIBC Visa20 $, 12,99 % : Visa Classique RBC avec option à taux d'intérêt réduit20 $, 13,99 % : Coast Capital Collabria Classic Mastercard25 $, 12,90 % : Carte Visa TD à faible taux25 $, 12,99 % : Carte de crédit Essentielle American Express25 $, 13,99 % : Coast Capital Collabria Low Rate Business Mastercard30 $, 12,99 % : Servus Personal Low Rate Mastercard39 $, 10,99 % : Mastercardᴹᴰ La Vraie Ligneᴹᴰ Or MBNA39 $, 20,99 % : WestJest Mastercard RBC39 $, 21,99 % : Mastercardᴹᴰ World Argent Content MBNA50 $, 12,99 % : Coast Capital Collabria Centra Gold Mastercard75 $, 20,99 % : Servus Personal Gold Mastercard99 $, 20,99 % : Remise en argent Préférence World Elite Mastercard RBC99 $, 21,99 % : Carte Dividendes Platine CIBC Visa100 $, 20,90 % : Remises World Elite Mastercard Desjardins110 $, 20,90 % : Odyssée Or Visa Desjardins119,88 $, 21,99 % : Carte sélecte RemiseSimple d'American Express120 $, 20,95 % : Mastercard World Elite Récompenses Tangerine120 $, 30,00 % : Carte Aéroplan American Express130 $, 20,90 % : Odyssée World Elite Mastercard Desjardins130 $, 20,99 % : Coast Capital Collabria World Elite Business Mastercard139 $, 20,99 % : WestJet World Elite Mastercard RBC150 $, 20,99 % : Servus Personal World Elite Mastercard165 $, 20,50 % : Visa Infinite British Airways RBC191,88 $, 21,99 % : Carte Cobalt American Express199 $, 21,99 % : Carte Mastercard BMO VIPorter World Elite250 $, 21,99 % : Carte Or avec primes American Express (Carte de crédit)250 $, 30,00 % : Carte Or avec primes American Express (carte de paiement)395 $, 11,90 % : Odyssée Visa Infinite Privilege Desjardins399 $, 20,99 % : Avion Visa Infinite Privilège RBC499 $, 21,99 % : Carte Aventura CIBC Visa Infinite Privilege599 $, 30,00 % : Carte Prestige Aéroplan American Express (Carte de paiement)799 $, 30,00 % : La Carte de Platine
Voir les données
Cartes de crédit répertoriées pour l'Alberta dans l'outil de comparaison de l'ACFC : frais annuels et taux d'intérêt sur les achats
TypeFrais annuelsTaux sur les achatsCartes
Avec récompenses0 $19,99 %1
Avec récompenses0 $20,90 %3
Avec récompenses0 $20,95 %2
Avec récompenses0 $20,99 %7
Avec récompenses0 $21,74 %1
Avec récompenses0 $21,75 %1
Avec récompenses0 $21,90 %3
Avec récompenses0 $21,99 %15
Avec récompenses39 $20,99 %1
Avec récompenses39 $21,99 %1
Avec récompenses50 $12,99 %1
Avec récompenses75 $20,99 %1
Avec récompenses89 $21,99 %3
Avec récompenses99 $20,99 %1
Avec récompenses99 $21,99 %1
Avec récompenses100 $20,90 %1
Avec récompenses110 $20,90 %1
Avec récompenses119,88 $21,99 %1
Avec récompenses120 $20,95 %1
Avec récompenses120 $20,99 %4
Avec récompenses120 $21,99 %3
Avec récompenses120 $30,00 %1
Avec récompenses130 $20,90 %1
Avec récompenses130 $20,99 %1
Avec récompenses139 $20,99 %1
Avec récompenses139 $21,99 %7
Avec récompenses150 $20,99 %1
Avec récompenses150 $21,99 %3
Avec récompenses165 $20,50 %1
Avec récompenses191,88 $21,99 %1
Avec récompenses199 $21,99 %1
Avec récompenses250 $21,99 %1
Avec récompenses250 $30,00 %1
Avec récompenses395 $11,90 %1
Avec récompenses399 $20,99 %1
Avec récompenses499 $21,99 %1
Avec récompenses599 $21,99 %4
Avec récompenses599 $30,00 %1
Avec récompenses799 $30,00 %1
Sans récompenses0 $10,90 %1
Sans récompenses0 $12,99 %1
Sans récompenses0 $21,99 %1
Sans récompenses0 $29,90 %2
Sans récompenses20 $12,99 %1
Sans récompenses20 $13,99 %1
Sans récompenses25 $12,90 %1
Sans récompenses25 $12,99 %1
Sans récompenses25 $13,99 %1
Sans récompenses29 $13,99 %3
Sans récompenses30 $12,99 %1
Sans récompenses39 $10,99 %1
Source : https://itools-ioutils.fcac-acfc.gc.ca/CCCT-OCCC/SearchFilter-fra.aspx, interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "fcac_search_credit_cards", "arguments": {"province": "AB", "limit": 100, "lang": "fr"}}
Pas de script pour cet appel. reproduce_code : This is a web page the tool parses, not a data table, so a download does not reproduce the result. Cite the URL and retrieval date, or keep the tool's output as the raw input.

6 434

jours de rendements obligataires pour les macroéconomistes, qui lisent la courbe avant le virage,

Quand la courbe des taux s'inverse

L'écart entre les rendements des obligations de référence du gouvernement du Canada à 10 ans et à 2 ans, chaque jour ouvrable depuis 2001, d'après l'API Valet de la Banque du Canada, tracé en moyenne mensuelle. Quand le 2 ans rapporte plus que le 10 ans, la courbe est inversée : les marchés s'attendent à une baisse des taux, le plus souvent parce qu'ils prévoient un ralentissement. Elle s'est inversée en 2007, 2019 à 2020 et 2022 à 2024. Le mois le plus creux a été juillet 2023, à -123 pb ; en septembre 2026, il était en moyenne de 62 pb.

La courbe des taux : rendement des obligations de référence du gouvernement du Canada à 10 ans moins celui à 2 ans, moyenne mensuelle des valeurs quotidiennes, mois inversés ombrés-200 pb-100 pb0 pb100 pb200 pb300 pbInversée20052010201520202025janvier 2001 : 41 pbfévrier 2001 : 50 pbmars 2001 : 63 pbavril 2001 : 82 pbmai 2001 : 98 pbjuin 2001 : 92 pbjuillet 2001 : 98 pbaoût 2001 : 107 pbseptembre 2001 : 163 pboctobre 2001 : 187 pbnovembre 2001 : 207 pbdécembre 2001 : 213 pbjanvier 2002 : 225 pbfévrier 2002 : 207 pbmars 2002 : 161 pbavril 2002 : 139 pbmai 2002 : 144 pbjuin 2002 : 141 pbjuillet 2002 : 171 pbaoût 2002 : 183 pbseptembre 2002 : 150 pboctobre 2002 : 165 pbnovembre 2002 : 176 pbdécembre 2002 : 170 pbjanvier 2003 : 166 pbfévrier 2003 : 153 pbmars 2003 : 132 pbavril 2003 : 131 pbmai 2003 : 124 pbjuin 2003 : 131 pbjuillet 2003 : 170 pbaoût 2003 : 188 pbseptembre 2003 : 172 pboctobre 2003 : 169 pbnovembre 2003 : 161 pbdécembre 2003 : 165 pbjanvier 2004 : 183 pbfévrier 2004 : 199 pbmars 2004 : 195 pbavril 2004 : 188 pbmai 2004 : 181 pbjuin 2004 : 163 pbjuillet 2004 : 160 pbaoût 2004 : 165 pbseptembre 2004 : 147 pboctobre 2004 : 133 pbnovembre 2004 : 126 pbdécembre 2004 : 138 pbjanvier 2005 : 131 pbfévrier 2005 : 129 pbmars 2005 : 122 pbavril 2005 : 109 pbmai 2005 : 103 pbjuin 2005 : 98 pbjuillet 2005 : 88 pbaoût 2005 : 81 pbseptembre 2005 : 74 pboctobre 2005 : 59 pbnovembre 2005 : 40 pbdécembre 2005 : 23 pbjanvier 2006 : 24 pbfévrier 2006 : 22 pbmars 2006 : 26 pbavril 2006 : 32 pbmai 2006 : 29 pbjuin 2006 : 17 pbjuillet 2006 : 21 pbaoût 2006 : 16 pbseptembre 2006 : 12 pboctobre 2006 : 9 pbnovembre 2006 : 6 pbdécembre 2006 : 6 pbjanvier 2007 : 7 pbfévrier 2007 : 4 pbmars 2007 : 10 pbavril 2007 : 7 pbmai 2007 : -3 pbjuin 2007 : -5 pbjuillet 2007 : -5 pbaoût 2007 : 8 pbseptembre 2007 : 15 pboctobre 2007 : 16 pbnovembre 2007 : 32 pbdécembre 2007 : 27 pbjanvier 2008 : 54 pbfévrier 2008 : 77 pbmars 2008 : 93 pbavril 2008 : 84 pbmai 2008 : 75 pbjuin 2008 : 58 pbjuillet 2008 : 62 pbaoût 2008 : 80 pbseptembre 2008 : 82 pboctobre 2008 : 145 pbnovembre 2008 : 171 pbdécembre 2008 : 157 pbjanvier 2009 : 167 pbfévrier 2009 : 175 pbmars 2009 : 186 pbavril 2009 : 189 pbmai 2009 : 208 pbjuin 2009 : 217 pbjuillet 2009 : 218 pbaoût 2009 : 213 pbseptembre 2009 : 211 pboctobre 2009 : 196 pbnovembre 2009 : 209 pbdécembre 2009 : 213 pbjanvier 2010 : 221 pbfévrier 2010 : 210 pbmars 2010 : 192 pbavril 2010 : 177 pbmai 2010 : 165 pbjuin 2010 : 163 pbjuillet 2010 : 162 pbaoût 2010 : 160 pbseptembre 2010 : 147 pboctobre 2010 : 139 pbnovembre 2010 : 145 pbdécembre 2010 : 154 pbjanvier 2011 : 153 pbfévrier 2011 : 158 pbmars 2011 : 152 pbavril 2011 : 152 pbmai 2011 : 151 pbjuin 2011 : 152 pbjuillet 2011 : 145 pbaoût 2011 : 146 pbseptembre 2011 : 128 pboctobre 2011 : 129 pbnovembre 2011 : 119 pbdécembre 2011 : 110 pbjanvier 2012 : 100 pbfévrier 2012 : 96 pbmars 2012 : 91 pbavril 2012 : 77 pbmai 2012 : 71 pbjuin 2012 : 74 pbjuillet 2012 : 66 pbaoût 2012 : 67 pbseptembre 2012 : 69 pboctobre 2012 : 71 pbnovembre 2012 : 65 pbdécembre 2012 : 67 pbjanvier 2013 : 76 pbfévrier 2013 : 86 pbmars 2013 : 88 pbavril 2013 : 79 pbmai 2013 : 89 pbjuin 2013 : 110 pbjuillet 2013 : 129 pbaoût 2013 : 143 pbseptembre 2013 : 145 pboctobre 2013 : 136 pbnovembre 2013 : 145 pbdécembre 2013 : 157 pbjanvier 2014 : 150 pbfévrier 2014 : 142 pbmars 2014 : 141 pbavril 2014 : 138 pbmai 2014 : 126 pbjuin 2014 : 121 pbjuillet 2014 : 110 pbaoût 2014 : 98 pbseptembre 2014 : 104 pboctobre 2014 : 99 pbnovembre 2014 : 100 pbdécembre 2014 : 84 pbjanvier 2015 : 77 pbfévrier 2015 : 94 pbmars 2015 : 89 pbavril 2015 : 83 pbmai 2015 : 107 pbjuin 2015 : 117 pbjuillet 2015 : 113 pbaoût 2015 : 99 pbseptembre 2015 : 100 pboctobre 2015 : 93 pbnovembre 2015 : 101 pbdécembre 2015 : 92 pbjanvier 2016 : 87 pbfévrier 2016 : 70 pbmars 2016 : 72 pbavril 2016 : 73 pbmai 2016 : 75 pbjuin 2016 : 64 pbjuillet 2016 : 51 pbaoût 2016 : 49 pbseptembre 2016 : 53 pboctobre 2016 : 60 pbnovembre 2016 : 81 pbdécembre 2016 : 96 pbjanvier 2017 : 95 pbfévrier 2017 : 94 pbmars 2017 : 93 pbavril 2017 : 79 pbmai 2017 : 82 pbjuin 2017 : 64 pbjuillet 2017 : 69 pbaoût 2017 : 64 pbseptembre 2017 : 52 pboctobre 2017 : 55 pbnovembre 2017 : 48 pbdécembre 2017 : 34 pbjanvier 2018 : 42 pbfévrier 2018 : 51 pbmars 2018 : 39 pbavril 2018 : 39 pbmai 2018 : 41 pbjuin 2018 : 34 pbjuillet 2018 : 22 pbaoût 2018 : 19 pbseptembre 2018 : 21 pboctobre 2018 : 20 pbnovembre 2018 : 14 pbdécembre 2018 : 8 pbjanvier 2019 : 8 pbfévrier 2019 : 12 pbmars 2019 : 10 pbavril 2019 : 14 pbmai 2019 : 8 pbjuin 2019 : 5 pbjuillet 2019 : -0 pbaoût 2019 : -16 pbseptembre 2019 : -18 pboctobre 2019 : -13 pbnovembre 2019 : -7 pbdécembre 2019 : -6 pbjanvier 2020 : -9 pbfévrier 2020 : -12 pbmars 2020 : 20 pbavril 2020 : 29 pbmai 2020 : 26 pbjuin 2020 : 26 pbjuillet 2020 : 24 pbaoût 2020 : 28 pbseptembre 2020 : 30 pboctobre 2020 : 36 pbnovembre 2020 : 42 pbdécembre 2020 : 49 pbjanvier 2021 : 64 pbfévrier 2021 : 89 pbmars 2021 : 124 pbavril 2021 : 124 pbmai 2021 : 121 pbjuin 2021 : 105 pbjuillet 2021 : 79 pbaoût 2021 : 74 pbseptembre 2021 : 82 pboctobre 2021 : 82 pbnovembre 2021 : 69 pbdécembre 2021 : 47 pbjanvier 2022 : 58 pbfévrier 2022 : 44 pbmars 2022 : 29 pbavril 2022 : 24 pbmai 2022 : 27 pbjuin 2022 : 16 pbjuillet 2022 : -13 pbaoût 2022 : -50 pbseptembre 2022 : -58 pboctobre 2022 : -63 pbnovembre 2022 : -79 pbdécembre 2022 : -87 pbjanvier 2023 : -80 pbfévrier 2023 : -88 pbmars 2023 : -83 pbavril 2023 : -81 pbmai 2023 : -88 pbjuin 2023 : -119 pbjuillet 2023 : -123 pbaoût 2023 : -105 pbseptembre 2023 : -97 pboctobre 2023 : -72 pbnovembre 2023 : -71 pbdécembre 2023 : -78 pbjanvier 2024 : -66 pbfévrier 2024 : -68 pbmars 2024 : -69 pbavril 2024 : -56 pbmai 2024 : -58 pbjuin 2024 : -55 pbjuillet 2024 : -37 pbaoût 2024 : -21 pbseptembre 2024 : -4 pboctobre 2024 : 11 pbnovembre 2024 : 10 pbdécembre 2024 : 17 pbjanvier 2025 : 36 pbfévrier 2025 : 37 pbmars 2025 : 47 pbavril 2025 : 58 pbmai 2025 : 63 pbjuin 2025 : 66 pbjuillet 2025 : 72 pbaoût 2025 : 73 pbseptembre 2025 : 72 pboctobre 2025 : 71 pbnovembre 2025 : 73 pbdécembre 2025 : 81 pbjanvier 2026 : 84 pbfévrier 2026 : 80 pbmars 2026 : 68 pbavril 2026 : 65 pbmai 2026 : 61 pbjuin 2026 : 63 pbjuillet 2026 : 71 pbaoût 2026 : 72 pbseptembre 2026 : 62 pb62 pbLa courbe des taux : rendement des obligations de référence du gouvernement du Canada à 10 ans moins celui à 2 ans, moyenne mensuelle des valeurs quotidiennes, mois inversés ombrés-200 pb-100 pb0 pb100 pb200 pb300 pbInversée20052010201520202025janvier 2001 : 41 pbfévrier 2001 : 50 pbmars 2001 : 63 pbavril 2001 : 82 pbmai 2001 : 98 pbjuin 2001 : 92 pbjuillet 2001 : 98 pbaoût 2001 : 107 pbseptembre 2001 : 163 pboctobre 2001 : 187 pbnovembre 2001 : 207 pbdécembre 2001 : 213 pbjanvier 2002 : 225 pbfévrier 2002 : 207 pbmars 2002 : 161 pbavril 2002 : 139 pbmai 2002 : 144 pbjuin 2002 : 141 pbjuillet 2002 : 171 pbaoût 2002 : 183 pbseptembre 2002 : 150 pboctobre 2002 : 165 pbnovembre 2002 : 176 pbdécembre 2002 : 170 pbjanvier 2003 : 166 pbfévrier 2003 : 153 pbmars 2003 : 132 pbavril 2003 : 131 pbmai 2003 : 124 pbjuin 2003 : 131 pbjuillet 2003 : 170 pbaoût 2003 : 188 pbseptembre 2003 : 172 pboctobre 2003 : 169 pbnovembre 2003 : 161 pbdécembre 2003 : 165 pbjanvier 2004 : 183 pbfévrier 2004 : 199 pbmars 2004 : 195 pbavril 2004 : 188 pbmai 2004 : 181 pbjuin 2004 : 163 pbjuillet 2004 : 160 pbaoût 2004 : 165 pbseptembre 2004 : 147 pboctobre 2004 : 133 pbnovembre 2004 : 126 pbdécembre 2004 : 138 pbjanvier 2005 : 131 pbfévrier 2005 : 129 pbmars 2005 : 122 pbavril 2005 : 109 pbmai 2005 : 103 pbjuin 2005 : 98 pbjuillet 2005 : 88 pbaoût 2005 : 81 pbseptembre 2005 : 74 pboctobre 2005 : 59 pbnovembre 2005 : 40 pbdécembre 2005 : 23 pbjanvier 2006 : 24 pbfévrier 2006 : 22 pbmars 2006 : 26 pbavril 2006 : 32 pbmai 2006 : 29 pbjuin 2006 : 17 pbjuillet 2006 : 21 pbaoût 2006 : 16 pbseptembre 2006 : 12 pboctobre 2006 : 9 pbnovembre 2006 : 6 pbdécembre 2006 : 6 pbjanvier 2007 : 7 pbfévrier 2007 : 4 pbmars 2007 : 10 pbavril 2007 : 7 pbmai 2007 : -3 pbjuin 2007 : -5 pbjuillet 2007 : -5 pbaoût 2007 : 8 pbseptembre 2007 : 15 pboctobre 2007 : 16 pbnovembre 2007 : 32 pbdécembre 2007 : 27 pbjanvier 2008 : 54 pbfévrier 2008 : 77 pbmars 2008 : 93 pbavril 2008 : 84 pbmai 2008 : 75 pbjuin 2008 : 58 pbjuillet 2008 : 62 pbaoût 2008 : 80 pbseptembre 2008 : 82 pboctobre 2008 : 145 pbnovembre 2008 : 171 pbdécembre 2008 : 157 pbjanvier 2009 : 167 pbfévrier 2009 : 175 pbmars 2009 : 186 pbavril 2009 : 189 pbmai 2009 : 208 pbjuin 2009 : 217 pbjuillet 2009 : 218 pbaoût 2009 : 213 pbseptembre 2009 : 211 pboctobre 2009 : 196 pbnovembre 2009 : 209 pbdécembre 2009 : 213 pbjanvier 2010 : 221 pbfévrier 2010 : 210 pbmars 2010 : 192 pbavril 2010 : 177 pbmai 2010 : 165 pbjuin 2010 : 163 pbjuillet 2010 : 162 pbaoût 2010 : 160 pbseptembre 2010 : 147 pboctobre 2010 : 139 pbnovembre 2010 : 145 pbdécembre 2010 : 154 pbjanvier 2011 : 153 pbfévrier 2011 : 158 pbmars 2011 : 152 pbavril 2011 : 152 pbmai 2011 : 151 pbjuin 2011 : 152 pbjuillet 2011 : 145 pbaoût 2011 : 146 pbseptembre 2011 : 128 pboctobre 2011 : 129 pbnovembre 2011 : 119 pbdécembre 2011 : 110 pbjanvier 2012 : 100 pbfévrier 2012 : 96 pbmars 2012 : 91 pbavril 2012 : 77 pbmai 2012 : 71 pbjuin 2012 : 74 pbjuillet 2012 : 66 pbaoût 2012 : 67 pbseptembre 2012 : 69 pboctobre 2012 : 71 pbnovembre 2012 : 65 pbdécembre 2012 : 67 pbjanvier 2013 : 76 pbfévrier 2013 : 86 pbmars 2013 : 88 pbavril 2013 : 79 pbmai 2013 : 89 pbjuin 2013 : 110 pbjuillet 2013 : 129 pbaoût 2013 : 143 pbseptembre 2013 : 145 pboctobre 2013 : 136 pbnovembre 2013 : 145 pbdécembre 2013 : 157 pbjanvier 2014 : 150 pbfévrier 2014 : 142 pbmars 2014 : 141 pbavril 2014 : 138 pbmai 2014 : 126 pbjuin 2014 : 121 pbjuillet 2014 : 110 pbaoût 2014 : 98 pbseptembre 2014 : 104 pboctobre 2014 : 99 pbnovembre 2014 : 100 pbdécembre 2014 : 84 pbjanvier 2015 : 77 pbfévrier 2015 : 94 pbmars 2015 : 89 pbavril 2015 : 83 pbmai 2015 : 107 pbjuin 2015 : 117 pbjuillet 2015 : 113 pbaoût 2015 : 99 pbseptembre 2015 : 100 pboctobre 2015 : 93 pbnovembre 2015 : 101 pbdécembre 2015 : 92 pbjanvier 2016 : 87 pbfévrier 2016 : 70 pbmars 2016 : 72 pbavril 2016 : 73 pbmai 2016 : 75 pbjuin 2016 : 64 pbjuillet 2016 : 51 pbaoût 2016 : 49 pbseptembre 2016 : 53 pboctobre 2016 : 60 pbnovembre 2016 : 81 pbdécembre 2016 : 96 pbjanvier 2017 : 95 pbfévrier 2017 : 94 pbmars 2017 : 93 pbavril 2017 : 79 pbmai 2017 : 82 pbjuin 2017 : 64 pbjuillet 2017 : 69 pbaoût 2017 : 64 pbseptembre 2017 : 52 pboctobre 2017 : 55 pbnovembre 2017 : 48 pbdécembre 2017 : 34 pbjanvier 2018 : 42 pbfévrier 2018 : 51 pbmars 2018 : 39 pbavril 2018 : 39 pbmai 2018 : 41 pbjuin 2018 : 34 pbjuillet 2018 : 22 pbaoût 2018 : 19 pbseptembre 2018 : 21 pboctobre 2018 : 20 pbnovembre 2018 : 14 pbdécembre 2018 : 8 pbjanvier 2019 : 8 pbfévrier 2019 : 12 pbmars 2019 : 10 pbavril 2019 : 14 pbmai 2019 : 8 pbjuin 2019 : 5 pbjuillet 2019 : -0 pbaoût 2019 : -16 pbseptembre 2019 : -18 pboctobre 2019 : -13 pbnovembre 2019 : -7 pbdécembre 2019 : -6 pbjanvier 2020 : -9 pbfévrier 2020 : -12 pbmars 2020 : 20 pbavril 2020 : 29 pbmai 2020 : 26 pbjuin 2020 : 26 pbjuillet 2020 : 24 pbaoût 2020 : 28 pbseptembre 2020 : 30 pboctobre 2020 : 36 pbnovembre 2020 : 42 pbdécembre 2020 : 49 pbjanvier 2021 : 64 pbfévrier 2021 : 89 pbmars 2021 : 124 pbavril 2021 : 124 pbmai 2021 : 121 pbjuin 2021 : 105 pbjuillet 2021 : 79 pbaoût 2021 : 74 pbseptembre 2021 : 82 pboctobre 2021 : 82 pbnovembre 2021 : 69 pbdécembre 2021 : 47 pbjanvier 2022 : 58 pbfévrier 2022 : 44 pbmars 2022 : 29 pbavril 2022 : 24 pbmai 2022 : 27 pbjuin 2022 : 16 pbjuillet 2022 : -13 pbaoût 2022 : -50 pbseptembre 2022 : -58 pboctobre 2022 : -63 pbnovembre 2022 : -79 pbdécembre 2022 : -87 pbjanvier 2023 : -80 pbfévrier 2023 : -88 pbmars 2023 : -83 pbavril 2023 : -81 pbmai 2023 : -88 pbjuin 2023 : -119 pbjuillet 2023 : -123 pbaoût 2023 : -105 pbseptembre 2023 : -97 pboctobre 2023 : -72 pbnovembre 2023 : -71 pbdécembre 2023 : -78 pbjanvier 2024 : -66 pbfévrier 2024 : -68 pbmars 2024 : -69 pbavril 2024 : -56 pbmai 2024 : -58 pbjuin 2024 : -55 pbjuillet 2024 : -37 pbaoût 2024 : -21 pbseptembre 2024 : -4 pboctobre 2024 : 11 pbnovembre 2024 : 10 pbdécembre 2024 : 17 pbjanvier 2025 : 36 pbfévrier 2025 : 37 pbmars 2025 : 47 pbavril 2025 : 58 pbmai 2025 : 63 pbjuin 2025 : 66 pbjuillet 2025 : 72 pbaoût 2025 : 73 pbseptembre 2025 : 72 pboctobre 2025 : 71 pbnovembre 2025 : 73 pbdécembre 2025 : 81 pbjanvier 2026 : 84 pbfévrier 2026 : 80 pbmars 2026 : 68 pbavril 2026 : 65 pbmai 2026 : 61 pbjuin 2026 : 63 pbjuillet 2026 : 71 pbaoût 2026 : 72 pbseptembre 2026 : 62 pb62 pb
Voir les données
L'écart de rendement entre 10 ans et 2 ans par année : moyenne, minimum et maximum des moyennes mensuelles de l'année
AnnéeMoyenneMois le plus basMois le plus haut
2001117 pb41 pb213 pb
2002169 pb139 pb225 pb
2003155 pb124 pb188 pb
2004165 pb126 pb199 pb
200588 pb23 pb131 pb
200618 pb6 pb32 pb
20079 pb-5 pb32 pb
200895 pb54 pb171 pb
2009200 pb167 pb218 pb
2010169 pb139 pb221 pb
2011141 pb110 pb158 pb
201276 pb65 pb100 pb
2013115 pb76 pb157 pb
2014118 pb84 pb150 pb
201597 pb77 pb117 pb
201669 pb49 pb96 pb
201769 pb34 pb95 pb
201829 pb8 pb51 pb
2019-0 pb-18 pb14 pb
202024 pb-12 pb49 pb
202188 pb47 pb124 pb
2022-13 pb-87 pb58 pb
2023-91 pb-123 pb-71 pb
2024-33 pb-69 pb17 pb
202563 pb36 pb81 pb
202670 pb61 pb84 pb
Source : https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json, interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "boc_get_observations", "arguments": {"series_names": ["BD.CDN.2YR.DQ.YLD", "BD.CDN.10YR.DQ.YLD"], "start_date": "2001-01-01"}}

R

# ============================================================
# Bank of Canada Valet: BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD
# Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
#          (exact: Valet request rebuilt from the tool's arguments)
# Inputs:  https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01
# Outputs: data/raw/valet_observations.json; the prepared table as `data`
# ============================================================

# 0. Setup ----

library(dplyr)
library(httr2)
library(janitor)
library(jsonlite)
library(lubridate)
library(readr)
library(stringr)
library(tibble)
library(tidyr)

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

response <- request("https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01") |>
  req_perform()

writeLines(resp_body_string(response), "data/raw/valet_observations.json")

payload <- fromJSON("data/raw/valet_observations.json", flatten = TRUE)
data <- as_tibble(payload[["observations"]])

# 2. Check inputs ----

stopifnot("https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01 returned no rows" = nrow(data) > 0)

# 3. Prepare data ----

data <- data |>
  clean_names()

# Valet nests each series as <series>.v; make one row per date and series.

data <- data |>
  pivot_longer(-d, names_to = "series", values_to = "value") |>
  mutate(
    series = str_remove(series, "_v$") |> str_to_upper(),
    value = as.numeric(value),
    date = ymd(d)
  ) |>
  select(date, series, value)

# Standard cleaning: trimmed text, empty strings as missing, and numbers
# stored as text converted to numbers.

data <- data |>
  mutate(across(where(is.character), \(x) na_if(str_trim(x), ""))) |>
  type_convert()

Python

# ============================================================
# Bank of Canada Valet: BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD
# Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
#          (exact: Valet request rebuilt from the tool's arguments)
# Inputs:  https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01
# Outputs: data/raw/valet_observations.json; the prepared table as `data`
# ============================================================

# %% 0. Setup

import json
import re
import unicodedata
from pathlib import Path

import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "valet_observations.json"

# %% 1. Read inputs

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=300,
    headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"},
) as client:
    response = client.get('https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01')
response.raise_for_status()
raw_path.write_bytes(response.content)

payload = json.loads(raw_path.read_text(encoding="utf-8"))
records = payload['observations']
data = pl.json_normalize(records, strict=False)

# %% 2. Check inputs

assert data.height > 0, "https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01 returned no rows"

# %% 3. Prepare data

# Valet nests each series as <series>.v; make one row per date and series.

data = data.unpivot(index="d", variable_name="series", value_name="value")
data = data.with_columns(
    pl.col("d").str.to_date().alias("date"),
    pl.col("series").str.replace(r"\.v$", ""),
    pl.col("value").cast(pl.Float64, strict=False),
).select("date", "series", "value")

# Standard cleaning: snake_case names without accents (PÉRIODE -> periode,
# referenceNumber -> reference_number, as janitor does in R), trimmed text,
# empty strings as missing. Names that clean alike are numbered as janitor
# numbers them (Indicator, indicator -> indicator, indicator_2).

clean_names = [
    re.sub(
        r"[^0-9a-z]+",
        "_",
        re.sub(
            r"([a-z0-9])([A-Z])",
            r"\1_\2",
            unicodedata.normalize("NFKD", column).encode("ascii", "ignore").decode(),
        ).lower(),
    ).strip("_")
    for column in data.columns
]
while len(set(clean_names)) < len(clean_names):
    name_counts = {}
    numbered = []
    for name in clean_names:
        name_counts[name] = name_counts.get(name, 0) + 1
        count = name_counts[name]
        numbered.append(name if count == 1 else f"{name}_{count}")
    clean_names = numbered
data = data.rename(dict(zip(data.columns, clean_names)))
data = data.with_columns(pl.col(pl.Utf8).str.strip_chars().replace("", None))

Stata

* ============================================================
* Bank of Canada Valet: BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD
* Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
*          (exact: Valet request rebuilt from the tool's arguments)
* Inputs:  https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01
* Outputs: data/raw/valet_observations.json; the prepared table as `data`
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/boc_get_observations.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
from pathlib import Path
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "valet_observations.json"
with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.get('https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01')
response.raise_for_status()
raw_path.write_bytes(response.content)
payload = json.loads(raw_path.read_text(encoding="utf-8"))
records = payload['observations']
data = pl.json_normalize(records, strict=False)
nested = [name for name, dtype in data.schema.items() if dtype.is_nested()]
data = data.with_columns(pl.col(name).map_elements(lambda value: json.dumps(value.to_list() if isinstance(value, pl.Series) else value, default=str), return_dtype=pl.Utf8) for name in nested)
data.write_csv(RAW_DIR / "valet_observations_prepared.csv")
end

import delimited "data/raw/valet_observations_prepared.csv", clear varnames(1) encoding("utf-8")

* 2. Check inputs

assert _N > 0

* 3. Prepare data

* One column per series (<series>_v), one row per date.

generate date = date(d, "YMD")
format date %td
drop d

* Standard cleaning: lower-case names, trimmed text, and numbers stored as
* text converted (destring leaves genuinely non-numeric text alone).
* rename *, lower stops at a clash (Indicator next to indicator), so names
* that lower-case alike are numbered as janitor numbers them (indicator,
* indicator_2), then renamed in one group rename, which allows swaps.

local names
foreach var of varlist _all {
    local names `names' `=strlower("`var'")'
}
local dups : list dups names
while "`dups'" != "" {
    local numbered
    local before
    foreach name of local names {
        local count 1
        foreach earlier of local before {
            if "`earlier'" == "`name'" local ++count
        }
        local before `before' `name'
        if `count' > 1 {
            local name = substr("`name'", 1, 32 - strlen("_`count'")) + "_`count'"
        }
        local numbered `numbered' `name'
    }
    local names `numbered'
    local dups : list dups names
}
local old_names
local new_names
local i 0
foreach var of varlist _all {
    local ++i
    local name : word `i' of `names'
    if "`name'" != "`var'" {
        local old_names `old_names' `var'
        local new_names `new_names' `name'
    }
}
if "`old_names'" != "" {
    rename (`old_names') (`new_names')
}
quietly ds, has(type string)
local text_vars `r(varlist)'
foreach var of local text_vars {
    replace `var' = strtrim(`var')
}
destring, replace

log close

Julia

# ============================================================
# Bank of Canada Valet: BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD
# Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
#          (exact: Valet request rebuilt from the tool's arguments)
# Inputs:  https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01
# Outputs: data/raw/valet_observations.json; the prepared table as `data`
# ============================================================

# 0. Setup

using DataFrames
using Downloads
using JSON3
using Tables
using TidierData

mkpath("data/raw")

# 1. Read inputs

Downloads.download("https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01", "data/raw/valet_observations.json")
payload = JSON3.read(read("data/raw/valet_observations.json", String))
records = payload["observations"]
data = DataFrame(Tables.dictrowtable(records))

# 2. Check inputs

@assert nrow(data) > 0 "https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json?start_date=2001-01-01 returned no rows"

# 3. Prepare data

# Standard cleaning: snake_case names, trimmed text, empty strings as missing.

data = @chain data begin
    @clean_names
end
data = mapcols(
    col -> eltype(col) <: Union{Missing, AbstractString} ?
        [ismissing(x) || isempty(strip(x)) ? missing : strip(x) for x in col] : col,
    data,
)

1 610 458

brevets pour les scientifiques, qui vérifient qui est arrivé le premier,

L'apprentissage automatique au bureau des brevets

Les demandes de brevet canadiennes dans la classe CIB G06N, où se classe l'apprentissage automatique, par année de dépôt, d'après les données Horizons PI d'ISDE. De 24 en 2005 à un sommet de 667 en 2021. Les comptes viennent de la diffusion en bloc Horizons PI la plus récente au moment de l'enregistrement des appels, datée du 11 octobre 2024. Les demandes sont publiées environ 18 mois après leur dépôt ; cette diffusion ne contient donc qu'une partie de celles déposées de 2023 à 2025 (122 pour l'instant), et ces années sont exclues.

Demandes de brevet canadiennes dans la classe CIB G06N (apprentissage automatique et autres calculs fondés sur des modèles biologiques), par année de dépôtG06N0250500750G06N, 2005 : 242005G06N, 2006 : 152006G06N, 2007 : 242007G06N, 2008 : 392008G06N, 2009 : 182009G06N, 2010 : 242010G06N, 2011 : 122011G06N, 2012 : 262012G06N, 2013 : 362013G06N, 2014 : 462014G06N, 2015 : 562015G06N, 2016 : 772016G06N, 2017 : 1782017G06N, 2018 : 2562018G06N, 2019 : 4782019G06N, 2020 : 5292020G06N, 2021 : 6672021G06N, 2022 : 6542022Demandes de brevet canadiennes dans la classe CIB G06N (apprentissage automatique et autres calculs fondés sur des modèles biologiques), par année de dépôtG06N0250500750G06N, 2005 : 242005G06N, 2006 : 152006G06N, 2007 : 242007G06N, 2008 : 392008G06N, 2009 : 182009G06N, 2010 : 242010G06N, 2011 : 122011G06N, 2012 : 262012G06N, 2013 : 362013G06N, 2014 : 462014G06N, 2015 : 562015G06N, 2016 : 772016G06N, 2017 : 1782017G06N, 2018 : 2562018G06N, 2019 : 4782019G06N, 2020 : 5292020G06N, 2021 : 6672021G06N, 2022 : 6542022
Voir les données
Demandes de brevet canadiennes dans la classe CIB G06N (apprentissage automatique et autres calculs fondés sur des modèles biologiques), par année de dépôt
Année de dépôtG06N
200524
200615
200724
200839
200918
201024
201112
201226
201336
201446
201556
201677
2017178
2018256
2019478
2020529
2021667
2022654
Source : https://open.canada.ca/data/en/dataset/fe1dfbb9-0fc3-42ca-b2a9-6ca4c05dbac9, interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "ised_ip_horizons_search_patents", "arguments": {"filed_from": "1800-01-01", "limit": 1}}

21 autres appels semblables, avec d'autres arguments.

R

# ============================================================
# Canadian patent search (CIPO IP Horizons)
# Purpose: Fetch the data behind MapleStats MCP's ised_ip_horizons_search_patents
#          (exact: the IP Horizons tables the search used, then the tool's filters)
# Inputs:  https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip
# Outputs: data/raw/PT_main_1_to_2000000_2024-10-11.zip; the prepared table as `data`
# ============================================================

# 0. Setup ----

library(dplyr)
library(janitor)
library(purrr)
library(readr)
library(stringr)

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

files <- c(
  main_1 = "https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip",
  main_2 = "https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_2000001_to_4000000_2024-10-11.zip"
)

walk2(
  files,
  names(files),
  \(url, name) download.file(url, str_c("data/raw/", name, ".zip"), mode = "wb")
)

# Every column as text; headers are bilingual ("Patent Number - Numéro du
# brevet"), so keep the English half in snake_case.

tables <- names(files) |>
  map(
    \(name) unzip(str_c("data/raw/", name, ".zip"), exdir = "data/raw") |>
      read_delim(
        delim = "|",
        quote = "",
        na = c("", "NULL"),
        col_types = cols(.default = col_character()),
        trim_ws = TRUE
      ) |>
      rename_with(\(x) str_split_i(x, " - ", 1) |> make_clean_names())
  ) |>
  set_names(names(files))

patents <- bind_rows(tables[c("main_1", "main_2")]) |>
  filter(str_detect(filing_date, "^\\d{4}-\\d{2}-\\d{2}$"), filing_date >= "1800-01-01")
data <- patents |>
  arrange(desc(filing_date))

# 2. Check inputs ----

stopifnot("https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip returned no rows" = nrow(data) > 0)

# 3. Prepare data ----

data <- data |>
  clean_names()

# Standard cleaning: trimmed text, empty strings as missing, and numbers
# stored as text converted to numbers.

data <- data |>
  mutate(across(where(is.character), \(x) na_if(str_trim(x), ""))) |>
  type_convert()

Python

# ============================================================
# Canadian patent search (CIPO IP Horizons)
# Purpose: Fetch the data behind MapleStats MCP's ised_ip_horizons_search_patents
#          (exact: the IP Horizons tables the search used, then the tool's filters)
# Inputs:  https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip
# Outputs: data/raw/PT_main_1_to_2000000_2024-10-11.zip; the prepared table as `data`
# ============================================================

# %% 0. Setup

import re
import ssl
import unicodedata
import zipfile
from pathlib import Path

import certifi
import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "PT_main_1_to_2000000_2024-10-11.zip"

# %% 1. Read inputs

# CIPO's server omits its RapidSSL intermediate certificate; add it from the
# issuer on top of certifi's roots, so verification still anchors at a root.

intermediate = httpx.get("http://cacerts.rapidssl.com/RapidSSLTLSRSACAG1.crt").content
tls = ssl.create_default_context(cafile=certifi.where())
tls.load_verify_locations(cadata=ssl.DER_cert_to_PEM_cert(intermediate))

files = {
    'main_1': 'https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip',
    'main_2': 'https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_2000001_to_4000000_2024-10-11.zip',
}
tables = {}
with httpx.Client(verify=tls, timeout=900, follow_redirects=True) as client:
    for name, url in files.items():
        zip_path = RAW_DIR / f"{name}.zip"
        with client.stream("GET", url) as response, zip_path.open("wb") as handle:
            response.raise_for_status()
            for chunk in response.iter_bytes(1 << 20):
                handle.write(chunk)
        with zipfile.ZipFile(zip_path) as archive:
            member = archive.namelist()[0]
            archive.extract(member, RAW_DIR)

        # Every column as text; headers are bilingual ("Patent Number - Numéro
        # du brevet"), so keep the English half in snake_case.

        tables[name] = (
            pl.scan_csv(
                RAW_DIR / member,
                separator="|",
                quote_char=None,
                null_values=["NULL"],
                infer_schema=False,
            )
            .rename(
                lambda column: re.sub(
                    r"[^a-z0-9]+", "_", column.split(" - ")[0].strip().lower()
                ).strip("_")
            )
            .with_columns(pl.all().str.strip_chars())
        )

patents = pl.concat([tables[name] for name in ['main_1', 'main_2']]).filter(pl.col("filing_date").str.contains(r"^\d{4}-\d{2}-\d{2}$"), pl.col("filing_date") >= "1800-01-01")
data = patents.sort("filing_date", descending=True, nulls_last=True).collect()

# %% 2. Check inputs

assert data.height > 0, "https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip returned no rows"

# %% 3. Prepare data

# Standard cleaning: snake_case names without accents (PÉRIODE -> periode,
# referenceNumber -> reference_number, as janitor does in R), trimmed text,
# empty strings as missing. Names that clean alike are numbered as janitor
# numbers them (Indicator, indicator -> indicator, indicator_2).

clean_names = [
    re.sub(
        r"[^0-9a-z]+",
        "_",
        re.sub(
            r"([a-z0-9])([A-Z])",
            r"\1_\2",
            unicodedata.normalize("NFKD", column).encode("ascii", "ignore").decode(),
        ).lower(),
    ).strip("_")
    for column in data.columns
]
while len(set(clean_names)) < len(clean_names):
    name_counts = {}
    numbered = []
    for name in clean_names:
        name_counts[name] = name_counts.get(name, 0) + 1
        count = name_counts[name]
        numbered.append(name if count == 1 else f"{name}_{count}")
    clean_names = numbered
data = data.rename(dict(zip(data.columns, clean_names)))
data = data.with_columns(pl.col(pl.Utf8).str.strip_chars().replace("", None))

Stata

* ============================================================
* Canadian patent search (CIPO IP Horizons)
* Purpose: Fetch the data behind MapleStats MCP's ised_ip_horizons_search_patents
*          (exact: the IP Horizons tables the search used, then the tool's filters)
* Inputs:  https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip
* Outputs: data/raw/PT_main_1_to_2000000_2024-10-11.zip; the prepared table as `data`
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/ised_ip_horizons_search_patents.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
import re
import ssl
import zipfile
from pathlib import Path
import certifi
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "PT_main_1_to_2000000_2024-10-11.zip"
intermediate = httpx.get("http://cacerts.rapidssl.com/RapidSSLTLSRSACAG1.crt").content
tls = ssl.create_default_context(cafile=certifi.where())
tls.load_verify_locations(cadata=ssl.DER_cert_to_PEM_cert(intermediate))
files = {'main_1': 'https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip', 'main_2': 'https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_2000001_to_4000000_2024-10-11.zip'}
tables = {}
exec('with httpx.Client(verify=tls, timeout=900, follow_redirects=True) as client:\n    for name, url in files.items():\n        zip_path = RAW_DIR / f"{name}.zip"\n        with client.stream("GET", url) as response, zip_path.open("wb") as handle:\n            response.raise_for_status()\n            for chunk in response.iter_bytes(1 << 20):\n                handle.write(chunk)\n        with zipfile.ZipFile(zip_path) as archive:\n            member = archive.namelist()[0]\n            archive.extract(member, RAW_DIR)\n        tables[name] = (pl.scan_csv(RAW_DIR / member, separator="|", quote_char=None, null_values=["NULL"], infer_schema=False) .rename(lambda column: re.sub(r"[^a-z0-9]+", "_", column.split(" - ")[0].strip().lower()).strip("_")) .with_columns(pl.all().str.strip_chars()))\n')
patents = pl.concat([tables[name] for name in ['main_1', 'main_2']]).filter(pl.col("filing_date").str.contains('^\\d{4}-\\d{2}-\\d{2}\x24'), pl.col("filing_date") >= "1800-01-01")
data = patents.sort("filing_date", descending=True, nulls_last=True).collect()
nested = [name for name, dtype in data.schema.items() if dtype.is_nested()]
data = data.with_columns(pl.col(name).map_elements(lambda value: json.dumps(value.to_list() if isinstance(value, pl.Series) else value, default=str), return_dtype=pl.Utf8) for name in nested)
data.write_csv(RAW_DIR / "PT_main_1_to_2000000_202_prepared.csv")
end

import delimited "data/raw/PT_main_1_to_2000000_202_prepared.csv", clear varnames(1) encoding("utf-8")

* 2. Check inputs

assert _N > 0

* 3. Prepare data

* Standard cleaning: lower-case names, trimmed text, and numbers stored as
* text converted (destring leaves genuinely non-numeric text alone).
* rename *, lower stops at a clash (Indicator next to indicator), so names
* that lower-case alike are numbered as janitor numbers them (indicator,
* indicator_2), then renamed in one group rename, which allows swaps.

local names
foreach var of varlist _all {
    local names `names' `=strlower("`var'")'
}
local dups : list dups names
while "`dups'" != "" {
    local numbered
    local before
    foreach name of local names {
        local count 1
        foreach earlier of local before {
            if "`earlier'" == "`name'" local ++count
        }
        local before `before' `name'
        if `count' > 1 {
            local name = substr("`name'", 1, 32 - strlen("_`count'")) + "_`count'"
        }
        local numbered `numbered' `name'
    }
    local names `numbered'
    local dups : list dups names
}
local old_names
local new_names
local i 0
foreach var of varlist _all {
    local ++i
    local name : word `i' of `names'
    if "`name'" != "`var'" {
        local old_names `old_names' `var'
        local new_names `new_names' `name'
    }
}
if "`old_names'" != "" {
    rename (`old_names') (`new_names')
}
quietly ds, has(type string)
local text_vars `r(varlist)'
foreach var of local text_vars {
    replace `var' = strtrim(`var')
}
destring, replace

log close

Julia

# ============================================================
# Canadian patent search (CIPO IP Horizons)
# Purpose: Fetch the data behind MapleStats MCP's ised_ip_horizons_search_patents
#          (exact: the IP Horizons tables the search used, then the tool's filters)
# Inputs:  https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip
# Outputs: data/raw/PT_main_1_to_2000000_2024-10-11.zip; the prepared table as `data`
# ============================================================

# 0. Setup

using CSV
using DataFrames
using Downloads
using TidierData
using ZipFile

mkpath("data/raw")

# 1. Read inputs

files = Dict(
    "main_1" => "https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip",
    "main_2" => "https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_2000001_to_4000000_2024-10-11.zip",
)
tables = Dict{String, DataFrame}()
for (name, url) in files
    zip_path = "data/raw/$(name).zip"
    Downloads.download(url, zip_path)
    archive = ZipFile.Reader(zip_path)
    member = archive.files[1]
    csv_path = "data/raw/$(member.name)"
    write(csv_path, read(member))
    close(archive)

    # Every column as text; keep the English half of each bilingual header.

    table = CSV.read(csv_path, DataFrame; delim = '|', quoted = false, missingstring = "NULL", types = String)
    rename!(table, [lowercase(replace(strip(split(n, " - ")[1]), r"[^A-Za-z0-9]+" => "_")) for n in names(table)])
    tables[name] = mapcols(col -> [ismissing(x) ? missing : strip(x) for x in col], table)
end

patents = vcat([tables[n] for n in ["main_1", "main_2"]]...)
patents = filter(row -> (!ismissing(row.filing_date) && occursin(r"^\d{4}-\d{2}-\d{2}$", row.filing_date) && row.filing_date >= "1800-01-01"), patents)
data = sort(patents, "filing_date"; rev = true)

# 2. Check inputs

@assert nrow(data) > 0 "https://opic-cipo.ca/cipo/client_downloads/Patent_CSV_2024_10_11/PT_main_1_to_2000000_2024-10-11.zip returned no rows"

# 3. Prepare data

# Standard cleaning: snake_case names, trimmed text, empty strings as missing.

data = @chain data begin
    @clean_names
end
data = mapcols(
    col -> eltype(col) <: Union{Missing, AbstractString} ?
        [ismissing(x) || isempty(strip(x)) ? missing : strip(x) for x in col] : col,
    data,
)

15 937

séries de la Banque du Canada pour les analystes, condamnés à expliquer le taux,

Le taux directeur depuis 2015

Le taux cible du financement à un jour de la Banque du Canada, chaque jour ouvrable depuis janvier 2015 : 29 changements, d'un creux de 0,25 % à un sommet de 5,00 %. Le 24 septembre 2026, il s'établissait à 2,25 %.

Taux cible du financement à un jour de la Banque du Canada, quotidien depuis 20150,00 %2,00 %4,00 %6,00 %2015201620172018201920202021202220232024202520261er janvier 2015 : 1,00 %21 janvier 2015 : 0,75 %15 juillet 2015 : 0,50 %12 juillet 2017 : 0,75 %6 septembre 2017 : 1,00 %17 janvier 2018 : 1,25 %11 juillet 2018 : 1,50 %24 octobre 2018 : 1,75 %4 mars 2020 : 1,25 %16 mars 2020 : 0,75 %27 mars 2020 : 0,25 %3 mars 2022 : 0,50 %14 avril 2022 : 1,00 %2 juin 2022 : 1,50 %14 juillet 2022 : 2,50 %8 septembre 2022 : 3,25 %27 octobre 2022 : 3,75 %8 décembre 2022 : 4,25 %26 janvier 2023 : 4,50 %8 juin 2023 : 4,75 %13 juillet 2023 : 5,00 %6 juin 2024 : 4,75 %25 juillet 2024 : 4,50 %5 septembre 2024 : 4,25 %24 octobre 2024 : 3,75 %12 décembre 2024 : 3,25 %30 janvier 2025 : 3,00 %13 mars 2025 : 2,75 %18 septembre 2025 : 2,50 %30 octobre 2025 : 2,25 %24 septembre 2026 : 2,25 %2,25 %Taux cible du financement à un jour de la Banque du Canada, quotidien depuis 20150,00 %2,00 %4,00 %6,00 %2015201720192021202320251er janvier 2015 : 1,00 %21 janvier 2015 : 0,75 %15 juillet 2015 : 0,50 %12 juillet 2017 : 0,75 %6 septembre 2017 : 1,00 %17 janvier 2018 : 1,25 %11 juillet 2018 : 1,50 %24 octobre 2018 : 1,75 %4 mars 2020 : 1,25 %16 mars 2020 : 0,75 %27 mars 2020 : 0,25 %3 mars 2022 : 0,50 %14 avril 2022 : 1,00 %2 juin 2022 : 1,50 %14 juillet 2022 : 2,50 %8 septembre 2022 : 3,25 %27 octobre 2022 : 3,75 %8 décembre 2022 : 4,25 %26 janvier 2023 : 4,50 %8 juin 2023 : 4,75 %13 juillet 2023 : 5,00 %6 juin 2024 : 4,75 %25 juillet 2024 : 4,50 %5 septembre 2024 : 4,25 %24 octobre 2024 : 3,75 %12 décembre 2024 : 3,25 %30 janvier 2025 : 3,00 %13 mars 2025 : 2,75 %18 septembre 2025 : 2,50 %30 octobre 2025 : 2,25 %24 septembre 2026 : 2,25 %2,25 %
Voir les données
Taux cible du financement à un jour de la Banque du Canada : le premier jour, chaque jour où il a changé et le dernier jour
DateTaux
1er janvier 20151,00 %
21 janvier 20150,75 %
15 juillet 20150,50 %
12 juillet 20170,75 %
6 septembre 20171,00 %
17 janvier 20181,25 %
11 juillet 20181,50 %
24 octobre 20181,75 %
4 mars 20201,25 %
16 mars 20200,75 %
27 mars 20200,25 %
3 mars 20220,50 %
14 avril 20221,00 %
2 juin 20221,50 %
14 juillet 20222,50 %
8 septembre 20223,25 %
27 octobre 20223,75 %
8 décembre 20224,25 %
26 janvier 20234,50 %
8 juin 20234,75 %
13 juillet 20235,00 %
6 juin 20244,75 %
25 juillet 20244,50 %
5 septembre 20244,25 %
24 octobre 20243,75 %
12 décembre 20243,25 %
30 janvier 20253,00 %
13 mars 20252,75 %
18 septembre 20252,50 %
30 octobre 20252,25 %
24 septembre 20262,25 %
Source : https://www.bankofcanada.ca/valet/observations/V39079/json, interrogée le 27 septembre 2026
Les appels à l'origine de ce graphique
Requêtecall_tool
{"name": "boc_get_observations", "arguments": {"series_names": ["V39079"], "start_date": "2015-01-01"}}

R

# ============================================================
# Bank of Canada Valet: V39079
# Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
#          (exact: Valet request rebuilt from the tool's arguments)
# Inputs:  https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01
# Outputs: data/raw/valet_observations.json; the prepared table as `data`
# ============================================================

# 0. Setup ----

library(dplyr)
library(httr2)
library(janitor)
library(jsonlite)
library(lubridate)
library(readr)
library(stringr)
library(tibble)
library(tidyr)

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

# 1. Read inputs ----

response <- request("https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01") |>
  req_perform()

writeLines(resp_body_string(response), "data/raw/valet_observations.json")

payload <- fromJSON("data/raw/valet_observations.json", flatten = TRUE)
data <- as_tibble(payload[["observations"]])

# 2. Check inputs ----

stopifnot("https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01 returned no rows" = nrow(data) > 0)

# 3. Prepare data ----

data <- data |>
  clean_names()

# Valet nests each series as <series>.v; make one row per date and series.

data <- data |>
  pivot_longer(-d, names_to = "series", values_to = "value") |>
  mutate(
    series = str_remove(series, "_v$") |> str_to_upper(),
    value = as.numeric(value),
    date = ymd(d)
  ) |>
  select(date, series, value)

# Standard cleaning: trimmed text, empty strings as missing, and numbers
# stored as text converted to numbers.

data <- data |>
  mutate(across(where(is.character), \(x) na_if(str_trim(x), ""))) |>
  type_convert()

Python

# ============================================================
# Bank of Canada Valet: V39079
# Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
#          (exact: Valet request rebuilt from the tool's arguments)
# Inputs:  https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01
# Outputs: data/raw/valet_observations.json; the prepared table as `data`
# ============================================================

# %% 0. Setup

import json
import re
import unicodedata
from pathlib import Path

import httpx
import polars as pl

RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "valet_observations.json"

# %% 1. Read inputs

with httpx.Client(
    http2=True,
    follow_redirects=True,
    timeout=300,
    headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"},
) as client:
    response = client.get('https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01')
response.raise_for_status()
raw_path.write_bytes(response.content)

payload = json.loads(raw_path.read_text(encoding="utf-8"))
records = payload['observations']
data = pl.json_normalize(records, strict=False)

# %% 2. Check inputs

assert data.height > 0, "https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01 returned no rows"

# %% 3. Prepare data

# Valet nests each series as <series>.v; make one row per date and series.

data = data.unpivot(index="d", variable_name="series", value_name="value")
data = data.with_columns(
    pl.col("d").str.to_date().alias("date"),
    pl.col("series").str.replace(r"\.v$", ""),
    pl.col("value").cast(pl.Float64, strict=False),
).select("date", "series", "value")

# Standard cleaning: snake_case names without accents (PÉRIODE -> periode,
# referenceNumber -> reference_number, as janitor does in R), trimmed text,
# empty strings as missing.

data = data.rename(
    {
        column: re.sub(
            r"[^0-9a-z]+",
            "_",
            re.sub(
                r"([a-z0-9])([A-Z])",
                r"\1_\2",
                unicodedata.normalize("NFKD", column).encode("ascii", "ignore").decode(),
            ).lower(),
        ).strip("_")
        for column in data.columns
    }
)
data = data.with_columns(pl.col(pl.Utf8).str.strip_chars().replace("", None))

Stata

* ============================================================
* Bank of Canada Valet: V39079
* Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
*          (exact: Valet request rebuilt from the tool's arguments)
* Inputs:  https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01
* Outputs: data/raw/valet_observations.json; the prepared table as `data`
* ============================================================

version 18
clear all
set more off

* 0. Setup

capture mkdir "logs"
capture log close
log using "logs/boc_get_observations.log", replace
capture mkdir "data"
capture mkdir "data/raw"

* 1. Read inputs

* Stata reads no JSON or HTML and truncates long column names, so its
* built-in Python (Stata 16+) fetches, filters and writes a CSV. Point
* Stata at a Python with these packages first: python set exec <path>.

python:
import json
from pathlib import Path
import httpx
import polars as pl
RAW_DIR = Path("data/raw")
RAW_DIR.mkdir(parents=True, exist_ok=True)
raw_path = RAW_DIR / "valet_observations.json"
with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.get('https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01')
response.raise_for_status()
raw_path.write_bytes(response.content)
payload = json.loads(raw_path.read_text(encoding="utf-8"))
records = payload['observations']
data = pl.json_normalize(records, strict=False)
nested = [name for name, dtype in data.schema.items() if dtype.is_nested()]
data = data.with_columns(pl.col(name).map_elements(lambda value: json.dumps(value.to_list() if isinstance(value, pl.Series) else value, default=str), return_dtype=pl.Utf8) for name in nested)
data.write_csv(RAW_DIR / "valet_observations_prepared.csv")
end

import delimited "data/raw/valet_observations_prepared.csv", clear varnames(1) encoding("utf-8")

* 2. Check inputs

assert _N > 0

* 3. Prepare data

* One column per series (<series>_v), one row per date.

generate date = date(d, "YMD")
format date %td
drop d

* Standard cleaning: lower-case names, trimmed text, and numbers stored as
* text converted (destring leaves genuinely non-numeric text alone).

rename *, lower
quietly ds, has(type string)
local text_vars `r(varlist)'
foreach var of local text_vars {
    replace `var' = strtrim(`var')
}
destring, replace

log close

Julia

# ============================================================
# Bank of Canada Valet: V39079
# Purpose: Fetch the data behind MapleStats MCP's boc_get_observations
#          (exact: Valet request rebuilt from the tool's arguments)
# Inputs:  https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01
# Outputs: data/raw/valet_observations.json; the prepared table as `data`
# ============================================================

# 0. Setup

using DataFrames
using Downloads
using JSON3
using Tables
using TidierData

mkpath("data/raw")

# 1. Read inputs

Downloads.download("https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01", "data/raw/valet_observations.json")
payload = JSON3.read(read("data/raw/valet_observations.json", String))
records = payload["observations"]
data = DataFrame(Tables.dictrowtable(records))

# 2. Check inputs

@assert nrow(data) > 0 "https://www.bankofcanada.ca/valet/observations/V39079/json?start_date=2015-01-01 returned no rows"

# 3. Prepare data

# Standard cleaning: snake_case names, trimmed text, empty strings as missing.

data = @chain data begin
    @clean_names
end
data = mapcols(
    col -> eltype(col) <: Union{Missing, AbstractString} ?
        [ismissing(x) || isempty(strip(x)) ? missing : strip(x) for x in col] : col,
    data,
)

Un MCP pour que l'Agent les trouve, un MCP pour les réunir tous.

L'anneau : 30 éditeurs fédéraux à l'extérieur, les provinces et les villes dotées de sources locales à l'intérieur, et 215 outils en arcs par sujetACFC · AchatsCanada · ACIA · ARC · Banque du Canada · Borealis · Bureau de la concurrence · CCG · CCL · DPB · ECCC · Élections Canada · Gazette du Canada · ICIS · Infobase de la santé (ASPC) · InfoBase du GC · IRCC · ISDE · Marées du MPO · OpenParliament · Rappels · Régie de l'énergie · RNCan énergie · RNCan toponymes · RNCan zones brûlées · SCHL · Séismes Canada · Sénat · StatCan · Transports Canada · Colombie-Britannique · Surrey · Vancouver · Victoria · Alberta · Airdrie · Calgary · Edmonton · Grande Prairie · Lethbridge · Medicine Hat · Parkland · Red Deer · St. Albert · Strathcona · Sturgeon · Saskatchewan · Regina · Saskatoon · Manitoba · Winnipeg · Ontario · Aurora · Durham · Hamilton · Kitchener · London · Markham · Mississauga · Newmarket · Ottawa · Peel · Toronto · Waterloo · Windsor · York · Québec · Montréal · Nouveau-Brunswick · Nouvelle-Écosse · Halifax · Île-du-Prince-Édouard · Terre-Neuve-et-Labrador · Yukon · Territoires du Nord-Ouest · Statistique : statistiques et recensement (62)Provinces et villes : catalogues de données ouvertes, provincial, municipal (47)Argent et affaires : monnaie, prix et finances publiques, entreprises, PI et concurrence (31)Terre et énergie : agriculture et alimentation, environnement et risques naturels, énergie, géographie, transport et sécurité (31)Population : santé, logement, immigration (22)Parlement : parlement, droit et élections (20)Statistique 62Provinces et villes 47Argent et affaires 31Terre et énergie 31Population 22Parlement 20215outils,une connexion
Voir les données
Les 215 outils par sujet (les arcs) ; les outils propres à MapleStats ne comptent que dans le total
SujetOutils
Statistique : statistiques et recensement62
Provinces et villes : catalogues de données ouvertes, provincial, municipal47
Argent et affaires : monnaie, prix et finances publiques, entreprises, PI et concurrence31
Terre et énergie : agriculture et alimentation, environnement et risques naturels, énergie, géographie, transport et sécurité31
Population : santé, logement, immigration22
Parlement : parlement, droit et élections20

L'accès aux données et leur découverte ne sont plus le goulot d'étranglement.

Ne payez plus pour des chiffres que le Canada publie déjà gratuitement. Posez la question, obtenez le chiffre avec sa source et un script qui le récupère de nouveau, et consacrez votre temps à ce que vous voulez vraiment faire : le modèle, l'argument, la décision.

MCP signifie Model Context Protocol, une norme ouverte qui relie les agents IA aux outils et aux données. Voyez-le comme un port USB-C pour l'IA : tout agent qui le parle peut se brancher sur tout serveur qui le parle aussi. MapleStats est cette prise pour les données canadiennes : 215 outils d'organismes fédéraux, de provinces, de villes et de catalogues de données ouvertes, derrière une seule connexion.

Ce qu'une seule connexion atteint

  • 1 610 458brevets
  • 962 449fiches du recensement
  • 15 937séries de la Banque du Canada
  • 8 271tableaux de Statistique Canada
  • 158fichiers de microdonnées
  • 98cartes de crédit
  • 67tableaux d'immigration
  • 54tableaux sur le logement