BonQuery
  • Home
  • Dashboards
  • Left in the Cold
  • Data & Methods
    • About the Data
    • Daily Data Sources
    • Central Intake Validation
  • About
  • Accueil
  • Tableaux de bord
  • Laissés dans le froid
  • Données et méthodes
    • À propos des données
    • Sources des données quotidiennes
    • Validation de l’admission centrale
  • À propos
  • FR
  • EN

Tendances historiques

Réseau de refuges de Toronto — séries chronologiques pluriannuelles

Tendances historiques pluriannuelles du réseau de refuges de Toronto — itinérance active, sorties vers le logement et itinérance chronique de janvier 2018 à aujourd’hui, avec filtres interactifs.

Auteur(-trice)

Miriam Marling

data = FileAttachment("../data/shelter_flow.json").json()
populationGroups = [
  "All Population",
  ...Array.from(new Set(data.map(d => d.population_group)))
    .filter(g => g !== "All Population")
    .sort()
]

availableYears = [
  "Toutes",
  ...Array.from(new Set(data.map(d => d.flow_date.slice(0, 4)))).sort()
]

monthOptions = [
  {label: "Tous", value: "Tous"},
  ...Array.from({length: 12}, (_, i) => ({
    label: new Date(2000, i).toLocaleString("fr-CA", {month: "long"}),
    value: String(i + 1)
  }))
]
viewof selectedGroup = Inputs.select(populationGroups, {
  label: "Groupe de population", value: "All Population", width: 220
})
viewof selectedYear = Inputs.select(availableYears, {
  label: "Année", value: "Toutes", width: 120
})
viewof selectedMonth = Inputs.select(monthOptions, {
  label: "Mois", format: d => d.label, value: monthOptions[0], width: 160
})
filtered = data
  .filter(d => {
    const groupMatch = d.population_group === selectedGroup
    const yearMatch  = selectedYear === "Toutes" || d.flow_date.slice(0, 4) === selectedYear
    const monthMatch = selectedMonth.value === "Tous" ||
                       parseInt(d.flow_date.slice(5, 7)) === parseInt(selectedMonth.value)
    return groupMatch && yearMatch && monthMatch
  })
  .map(d => {
    const [y, m, day] = d.flow_date.split("-").map(Number)
    return {...d, date: new Date(y, m - 1, day), month: d.flow_date.slice(0, 7)}
  })
  .sort((a, b) => a.date - b.date)

MON = ["janv.","févr.","mars","avr.","mai","juin",
       "juill.","août","sept.","oct.","nov.","déc."]

spanMonths = filtered.length < 2 ? 1 :
  Math.round((filtered[filtered.length-1].date - filtered[0].date)
             / (30.44 * 24 * 3600 * 1000))

xLineDense  = spanMonths > 18 && filtered.length > 20
xLineSingle = spanMonths <= 18

xLine = ({
  type: "time",
  label: null,
  ticks: xLineSingle ? d3.utcMonth.every(1) : 8,
  tickFormat: d => {
    const m = d.getMonth(), y = d.getFullYear()
    if (xLineSingle)  return m === 0 ? `${MON[m]}-${y}` : MON[m]
    if (xLineDense)   return m === 0 ? String(y) : m === 6 ? `juill. ${y}` : ""
    return m === 0 ? `janv.-${y}` : m === 6 ? `juill.-${y}` : `${MON[m]}-${y}`
  }
})

monthStart = d => new Date(d.date.getFullYear(), d.date.getMonth(), 1)
monthEnd   = d => new Date(d.date.getFullYear(), d.date.getMonth() + 1, 1)
monthMid   = d => new Date(d.date.getFullYear(), d.date.getMonth(), 15)

fmtN   = d => d.toLocaleString("fr-CA")
fmtPct = d => (d * 100).toFixed(0) + " %"

La Ville de Toronto publie mensuellement des données sur son réseau de refuges, et ses propres tableaux de bord présentent bien les chiffres principaux. Cette section va plus loin : filtres interactifs sur chaque graphique, une note explicite sur ce que les données couvrent et ce qu’elles excluent, et des conclusions qui restent habituellement enfouies dans la documentation technique.

Les données couvrent chaque mois de janvier 2018 au mois le plus récemment publié, directement issues du jeu de données Flux du réseau de refuges de la Ville de Toronto. Les nouvelles données se chargent automatiquement chaque mois, généralement vers le 15, après que la Ville publie les chiffres du mois précédent.


Personnes en situation d’itinérance active au cours des 3 derniers mois

Plot.plot({
  width, height: 380, marginLeft: 70,
  x: xLine,
  y: {label: "Personnes en situation d'itinérance active", grid: true, tickFormat: fmtN},
  marks: [
    Plot.ruleY([0]),
    Plot.line(filtered, {x: "date", y: "actively_homeless", stroke: "#FF2D55", strokeWidth: 2}),
    Plot.dot(filtered,  {x: "date", y: "actively_homeless", fill:   "#FF2D55", r: 2}),
    Plot.tip(filtered, Plot.pointerX({
      x: "date", y: "actively_homeless",
      title: d => `${d.month}\n${d.actively_homeless.toLocaleString("fr-CA")} personnes`
    }))
  ]
})

Variation nette (entrées moins sorties) par mois

netData = filtered.map(d => ({
  date: d.date, month: d.month,
  net_change: d.newly_identified + d.returned_from_housing + d.returned_to_shelter
              - d.moved_to_housing - d.became_inactive
}))

Plot.plot({
  width, height: 380, marginLeft: 70,
  x: xLine,
  y: {label: "Variation nette (personnes)", grid: true, tickFormat: fmtN},
  marks: [
    Plot.ruleY([0], {stroke: getComputedStyle(document.body).getPropertyValue("--bq-fg-secondary"), strokeWidth: 1.5}),
    Plot.line(netData, {x: "date", y: "net_change", stroke: "#5856D6", strokeWidth: 2}),
    Plot.dot(netData,  {x: "date", y: "net_change", fill:   "#5856D6", r: 2}),
    Plot.tip(netData, Plot.pointerX({
      x: "date", y: "net_change",
      title: d => {
        const sign = d.net_change >= 0 ? "+" : ""
        return `${d.month}\n${sign}${d.net_change.toLocaleString("fr-CA")} personnes`
      }
    }))
  ]
})

Total des entrées et sorties par mois

totalRectData = filtered.map(d => {
  const inflow  = d.newly_identified + d.returned_from_housing + d.returned_to_shelter
  const outflow = d.moved_to_housing + d.became_inactive
  const lbl     = `${MON[parseInt(d.month.slice(5,7))-1]}-${d.month.slice(0,4)}`
  return {
    x1: monthStart(d), x2: monthEnd(d), xMid: monthMid(d),
    inflow, outflow,
    tipTitle: `${lbl}\nEntrées :  +${inflow.toLocaleString("fr-CA")}\nSorties : −${outflow.toLocaleString("fr-CA")}\nNet :      ${(inflow-outflow)>=0?"+":""}${(inflow-outflow).toLocaleString("fr-CA")}`
  }
})

Plot.plot({
  width, height: 380, marginLeft: 70,
  x: xLine,
  y: {label: "Personnes", grid: true, tickFormat: fmtN},
  color: {domain: ["Entrées","Sorties"], range: ["#E63946","#66CC66"], legend: true},
  marks: [
    Plot.ruleY([0]),
    Plot.rectY(totalRectData, {x1: "x1", x2: "x2", y1: 0, y2: "inflow",         fill: "#E63946", insetLeft: 0.5, insetRight: 0.5}),
    Plot.rectY(totalRectData, {x1: "x1", x2: "x2", y1: d => -d.outflow, y2: 0,  fill: "#66CC66", insetLeft: 0.5, insetRight: 0.5}),
    Plot.tip(totalRectData, Plot.pointerX({x: "xMid", title: d => d.tipTitle}))
  ]
})

Entrées et sorties détaillées par mois

detailedFlowTypes = [
  "Entrées – Nouvellement repérés",
  "Entrées – Retour du logement permanent",
  "Entrées – Retour au refuge",
  "Sorties – Devenus inactifs",
  "Sorties – Accès au logement permanent"
]

detailedColors = ["#E63946","#FFD600","#FB8C00","#512DA8","#66CC66"]

detailRectData = filtered.flatMap(d => {
  const x1 = monthStart(d), x2 = monthEnd(d), xMid = monthMid(d)
  const ni = d.newly_identified, rfh = d.returned_from_housing,
        rts = d.returned_to_shelter, bi = d.became_inactive, mh = d.moved_to_housing
  return [
    {x1, x2, xMid, type: "Entrées – Nouvellement repérés",           y1: 0,       y2: ni},
    {x1, x2, xMid, type: "Entrées – Retour du logement permanent",   y1: ni,      y2: ni+rfh},
    {x1, x2, xMid, type: "Entrées – Retour au refuge",               y1: ni+rfh,  y2: ni+rfh+rts},
    {x1, x2, xMid, type: "Sorties – Devenus inactifs",               y1: 0,       y2: -bi},
    {x1, x2, xMid, type: "Sorties – Accès au logement permanent",    y1: -bi,     y2: -(bi+mh)}
  ]
})

detailTipData = filtered.map(d => {
  const lbl = `${MON[parseInt(d.month.slice(5,7))-1]}-${d.month.slice(0,4)}`
  return {
    xMid: monthMid(d),
    tipTitle: [
      lbl,
      `Nouvellement repérés :       +${d.newly_identified.toLocaleString("fr-CA")}`,
      `Retour du logement perm. :   +${d.returned_from_housing.toLocaleString("fr-CA")}`,
      `Retour au refuge :           +${d.returned_to_shelter.toLocaleString("fr-CA")}`,
      `Devenus inactifs :           −${d.became_inactive.toLocaleString("fr-CA")}`,
      `Accès au logement perm. :    −${d.moved_to_housing.toLocaleString("fr-CA")}`
    ].join("\n")
  }
})

Plot.plot({
  width, height: 420, marginLeft: 70,
  x: xLine,
  y: {label: "Personnes", grid: true, tickFormat: fmtN},
  color: {domain: detailedFlowTypes, range: detailedColors, legend: true},
  marks: [
    Plot.ruleY([0]),
    Plot.rectY(detailRectData, {x1: "x1", x2: "x2", y1: "y1", y2: "y2", fill: "type", insetLeft: 0.5, insetRight: 0.5}),
    Plot.tip(detailTipData, Plot.pointerX({x: "xMid", title: d => d.tipTitle}))
  ]
})

Âge des personnes en situation d’itinérance active au cours des 3 derniers mois

ageBandDefs = [
  {key: "age_under_16", label: "Moins de 16 ans"},
  {key: "age_16_24",    label: "16–24"},
  {key: "age_25_34",    label: "25–34"},
  {key: "age_35_44",    label: "35–44"},
  {key: "age_45_54",    label: "45–54"},
  {key: "age_55_64",    label: "55–64"},
  {key: "age_65_over",  label: "65+"}
]

ageData = filtered.flatMap(d => {
  const total = ageBandDefs.reduce((s, b) => s + (d[b.key] || 0), 0)
  return ageBandDefs.map(b => ({
    date: d.date, month: d.month, band: b.label,
    pct: total > 0 ? d[b.key] / total : null
  }))
})

Plot.plot({
  width, height: 400, marginLeft: 60,
  x: xLine,
  y: {label: "Part du total par groupe d'âge", grid: true, tickFormat: fmtPct},
  color: {legend: true},
  marks: [
    Plot.ruleY([0]),
    Plot.line(ageData, {x: "date", y: "pct", stroke: "band", strokeWidth: 1.8}),
    Plot.tip(ageData, Plot.pointerX({
      x: "date", y: "pct", stroke: "band",
      title: d => {
        const lbl = `${MON[parseInt(d.month.slice(5,7))-1]} ${d.month.slice(0,4)}`
        return `${d.band} — ${lbl}\n${(d.pct * 100).toFixed(1)} %`
      }
    }))
  ]
})

Genre des personnes en situation d’itinérance active au cours des 3 derniers mois

genderDefs = [
  {key: "gender_male",                label: "Hommes"},
  {key: "gender_female",              label: "Femmes"},
  {key: "gender_trans_nb_two_spirit", label: "Trans, non binaires ou bispirituels"}
]

genderData = filtered.flatMap(d => {
  const total = genderDefs.reduce((s, g) => s + (d[g.key] || 0), 0)
  return genderDefs.map(g => ({
    date: d.date, month: d.month, gender: g.label,
    pct: total > 0 ? d[g.key] / total : null
  }))
})

Plot.plot({
  width, height: 400, marginLeft: 60,
  x: xLine,
  y: {label: "Part du total par genre", grid: true, tickFormat: fmtPct},
  color: {legend: true},
  marks: [
    Plot.ruleY([0]),
    Plot.line(genderData, {x: "date", y: "pct", stroke: "gender", strokeWidth: 1.8}),
    Plot.tip(genderData, Plot.pointerX({
      x: "date", y: "pct", stroke: "gender",
      title: d => {
        const lbl = `${MON[parseInt(d.month.slice(5,7))-1]} ${d.month.slice(0,4)}`
        return `${d.gender} — ${lbl}\n${(d.pct * 100).toFixed(1)} %`
      }
    }))
  ]
})

Consulter les autres tableaux de bord :

  • Portrait mensuel
  • Comparaison annuelle cumulative

Ce tableau de bord est également disponible en tant que tableau de bord Oracle APEX reposant sur la même base de données — utile pour voir comment les mêmes données s’affichent dans la plateforme native à faible code d’Oracle, ou pour explorer le SQL et la configuration APEX sous-jacents.

Comment lire ces données

Ces chiffres reflètent le réseau de refuges, et non l’ensemble du phénomène de l’itinérance. Les données recensent les personnes ayant utilisé un service d’hébergement de nuit financé par la Ville au moins une fois au cours des trois derniers mois. Elles n’incluent pas les personnes qui dorment à l’extérieur, dans des refuges non financés par la Ville, ou qui séjournent temporairement chez des amis ou de la famille. La Ville estime qu’environ 18 % des personnes en situation d’itinérance absolue à Toronto ne sont pas reflétées dans ces chiffres. Les tendances présentées ici reflètent les changements dans le réseau de refuges spécifiquement — et non dans l’ensemble de l’itinérance dans la ville.

Définitions clés

Les tableaux de bord utilisent quelques termes ayant des significations précises qu’il vaut la peine de connaître avant de lire les graphiques.

Itinérance active. Une personne ayant utilisé les services d’hébergement financés par la Ville au moins une fois au cours des trois derniers mois et dont le déménagement vers un logement permanent n’a pas été enregistré. C’est le chiffre principal de chaque tableau de bord. Comme il remonte à trois mois en arrière, ce dénombrement inclut des personnes qui peuvent ne pas être en refuge un soir donné.

Itinérance chronique. La définition fédérale : une personne ayant passé au moins 180 nuits en refuge au cours de l’année écoulée, ou au moins 546 nuits au cours des trois dernières années. Une personne peut satisfaire à cette définition tout en étant encore dans le réseau de refuges — elle n’a pas besoin d’être partie et revenue.

Catégories d’entrées (personnes intégrant le réseau de refuges ce mois-ci) :

  • Nouvellement repérés. Personnes entrant dans le réseau de refuges pour la première fois. Exception pour le groupe « Chronique » : dans ce cas, cette colonne comptabilise les personnes devenues en situation d’itinérance chronique au cours du mois de référence, quelle que soit la durée de leur utilisation antérieure du réseau.
  • Retour du logement permanent. Personnes ayant précédemment accédé à un logement permanent et revenues dans le réseau de refuges.
  • Retour au refuge. Personnes qui étaient dans le réseau, ne l’ont pas utilisé pendant au moins trois mois, et y sont revenues.

Catégories de sorties (personnes quittant le réseau de refuges ce mois-ci) :

  • Accès au logement permanent. Personnes ayant quitté le réseau de refuges pour un logement permanent.
  • Devenus inactifs. Personnes n’ayant pas utilisé les services d’hébergement au cours des trois derniers mois, y compris le mois de référence. Cela ne signifie pas que la personne a trouvé un logement.

Définitions basées sur la page Données sur les flux du réseau de refuges de la Ville de Toronto.

Lire toutes les conclusions →

Source des données

Toutes les données proviennent du jeu de données Flux du réseau de refuges de Toronto de la Ville de Toronto, publié mensuellement sur le portail de données ouvertes de la Ville.

Contient des informations sous licence Licence du gouvernement ouvert – Toronto.

Retour au sommet
Need a safe place or support now? These contacts serve Toronto unless another area is named. BonQuery is an independent data project, not a service provider.
Assaulted Women's HelplineFree and confidential, anywhere in Ontario, 24/7 1-866-863-0511 Young people: Kids Help PhoneAges 5 to 29, confidential, 24/7. Call, or text CONNECT to 686868. 1-800-668-6868 Thinking about suicide, or worried about someone?Call or text 988 anywhere in Canada, 24/7. 988 Toronto emergency shelter line416-338-4766 Toronto shelter line, toll-free1-877-338-3398 Toronto mental-health crisis support211 Toronto street outreach311 Toronto 311 from outside city limits416-392-2489 City services TTY relay711 Immediate danger in Canada911 Toronto warming-centre status
Besoin d'un endroit sûr ou de soutien maintenant? Ces services desservent Toronto, sauf si une autre région est indiquée. BonQuery est un projet de données indépendant, pas un fournisseur de services.
Ligne d'aide aux femmes victimes de violenceGratuite et confidentielle, partout en Ontario, 24 h/24 et 7 j/7 1-866-863-0511 Jeunes : Jeunesse, J'écouteDe 5 à 29 ans, service confidentiel 24 h/24 et 7 j/7. Appelez ou textez le 686868. 1-800-668-6868 Vous pensez au suicide ou vous vous inquiétez pour quelqu'un?Appelez ou textez le 988 partout au Canada, 24 h/24 et 7 j/7. 988 Ligne des refuges d'urgence de Toronto416-338-4766 Ligne des refuges de Toronto, sans frais1-877-338-3398 Soutien en cas de crise de santé mentale à Toronto211 Intervention de rue à Toronto311 Toronto 311 depuis l'extérieur de la ville416-392-2489 Service de relais ATS de la Ville711 Danger immédiat au Canada911 État des centres de réchauffement de Toronto
 

Analyse indépendante d’intérêt public du réseau de refuges de Toronto.
© 2026 Miriam Marling · BonQuery