Choose a month and see how many people used Toronto’s shelter system, entered or left the count, or moved into housing.
Author
Miriam Marling
data =FileAttachment("../data/shelter_flow.json").json()
MON = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]fmtN = d => d.toLocaleString()// Build month dropdown in descending order (most recent first).// Display: "March 2026" Return value: "2026-03-01" (the YYYY-MM-DD from DB)reportingMonthOptions = data.filter(d => d.population_group==="All Population").sort((a, b) => b.flow_date.localeCompare(a.flow_date)).map(d => {const [y, m] = d.flow_date.split("-").map(Number)return {label:newDate(y, m -1,15).toLocaleString("default", {month:"long",year:"numeric"}),value: d.flow_date } })
Toronto publishes new shelter numbers each month. Choose a month to see how many people were counted, how many entered or left the count, and how many moved into housing.
The page covers January 2018 to the newest month in the City’s public data. It updates after the City posts new numbers, usually around the 15th.
viewof reportingDate = Inputs.select(reportingMonthOptions, {label:"Reporting Month",format: d => d.label,value: reportingMonthOptions[0]})
These numbers do not count everyone without housing. They count people who used a City-funded overnight service at least once in the past three months. They miss people sleeping outside, using a shelter the City does not fund, or staying with friends or family. The City estimates that about 18% of people experiencing homelessness are missing from this count.
What the City’s labels mean
We use simpler labels in the charts. These are the City’s official terms.
Actively homeless: counted as homeless in the shelter system. This means the person used a City-funded overnight service in the past three months and was not recorded as moving to permanent housing. The person may not be in a shelter on the day shown.
Chronic homelessness: homeless for a long time. This means at least 180 nights in shelter during one year, or 546 nights during three years.
Added to the count this month:
Newly counted as homeless: first entered the shelter count.
Returned after permanent housing: came back after moving into housing.
Returned after three months away: used shelter again after not using it for at least three months.
Removed from the count this month:
Moved into permanent housing: recorded as leaving for housing.
Not seen in shelter for three months: removed because the person did not use a City-funded shelter during that time. This does not mean the person found housing.
html`<div class="crisis-hero"> <div class="hero-number">${_ratio}×</div> <div class="hero-text"> <div class="hero-label">more <strong>men</strong> than women were counted as homeless in Toronto's shelter system</div> <div class="hero-sub"><strong>${_menCount.toLocaleString()} men</strong> · ${_womenCount.toLocaleString()} women · ${_transCount.toLocaleString()} transgender, non-binary, or two-spirit · ${_flowMonth}</div> </div></div>`
_genderStats = [ {label:"Men",mean: _menMean,ci_lo: _menMean -1.96* _menSE,ci_hi: _menMean +1.96* _menSE,isFocus:true}, {label:"Women",mean: _womenMean,ci_lo: _womenMean -1.96* _womenSE,ci_hi: _womenMean +1.96* _womenSE,isFocus:false}, {label:"Trans/NB/2S",mean: _transMean,ci_lo: _transMean -1.96* _transSE,ci_hi: _transMean +1.96* _transSE,isFocus:false}].sort((a, b) => b.mean- a.mean)Plot.plot({ width,height:300,marginBottom:36,marginLeft:70,marginRight:20,title:`Average monthly shelter count by gender, ${_yearRange}`,caption:"The thin lines show the likely range around each average. Source: City of Toronto Shelter System Flow data.",x: {label:null,domain: _genderStats.map(d => d.label)},y: {label:"Average people counted each month",grid:true,tickFormat: d => d.toLocaleString(),domain: [0,Math.max(..._genderStats.map(d => d.ci_hi)) *1.12]},marks: [ Plot.barY(_genderStats, {x:"label",y:"mean",fill: d => d.isFocus?"#ff3b30":"#4a6fa5",fillOpacity: d => d.isFocus?1:0.55 }), Plot.ruleY(_genderStats, {x:"label",y1:"ci_lo",y2:"ci_hi",stroke: _chartStroke,strokeWidth:1.8 }), Plot.tickY(_genderStats, {x:"label",y:"ci_hi",stroke: _chartStroke,strokeWidth:1.8,insetLeft:18,insetRight:18 }), Plot.tickY(_genderStats, {x:"label",y:"ci_lo",stroke: _chartStroke,strokeWidth:1.8,insetLeft:18,insetRight:18 }), Plot.text(_genderStats, {x:"label",y:"ci_hi",text: d =>Math.round(d.mean).toLocaleString(),dy:-10,textAnchor:"middle",fontSize:13,fill: d => d.isFocus?"#cc1f15": _chartFg,fontWeight:"600" }), Plot.ruleY([0]) ]})
NoteShow the statistical details
{const bg = bayesian_genderconst rr = bg.rate_ratiosconst pd = bg.pairwise_diffsconst diag = bg.diagnosticsconst fmt1 = x =>Number(x).toFixed(1)const fmtN = x =>Math.round(x).toLocaleString()const bayesPairs = [ {comp:"Men vs Women",rr: rr.men_vs_women,diff: pd.men_vs_women,note:false}, {comp:"Men vs Trans/NB/2S",rr: rr.men_vs_transnb2s,diff: pd.men_vs_transnb2s,note:true}, {comp:"Women vs Trans/NB/2S",rr: rr.women_vs_transnb2s,diff: pd.women_vs_transnb2s,note:true} ]const bayesRows = bayesPairs.map(p =>html`<tr> <td style="padding:3px 10px 3px 0">${p.comp}${p.note?html`<sup> †</sup>`:""}</td> <td style="padding:3px 10px;text-align:right;font-variant-numeric:tabular-nums">${fmt1(p.rr.median)}× (${fmt1(p.rr.hdi[0])}–${fmt1(p.rr.hdi[1])}) </td> <td style="padding:3px 0;text-align:right;font-variant-numeric:tabular-nums;font-weight:${p.note?"400":"700"}">${fmtN(p.diff.median)} (${fmtN(p.diff.hdi[0])}–${fmtN(p.diff.hdi[1])}) </td> </tr>`)returnhtml`<div class="bq-bayes"> <p style="margin:0 0 0.3rem"> <strong>Bayesian analysis (negative-binomial regression, fit with <code>brms</code> ${bg.brms_version} on ${bg.n_months} months of data):</strong> Weakly informative priors were used throughout. Sampling diagnostics: R̂ ${diag.max_rhat}, ESS ${diag.min_ess_bulk.toLocaleString()},${diag.n_divergent} divergent transitions. </p> <table> <thead><tr> <th style="text-align:left;padding:3px 10px 3px 0;font-weight:600">Comparison</th> <th style="text-align:right;padding:3px 10px;font-weight:600">Rate ratio (95% HDI)</th> <th style="text-align:right;padding:3px 0;font-weight:600">Difference in count (95% HDI)</th> </tr></thead> <tbody>${bayesRows}</tbody> </table> <p class="bq-note" style="margin:0 0 0.5rem"> † The Trans/NB/2S group averages ~${Math.round(_transMean)} individuals per month, a small count that is also likely under-reported in administrative shelter records. Tests involving this group should be interpreted with caution. </p> <p style="margin:0 0 0.3rem"> <strong>Men vs Women:</strong> Men's expected monthly count is roughly <strong>${fmt1(rr.men_vs_women.median)}× that of women</strong> (95% HDI ${fmt1(rr.men_vs_women.hdi[0])}–${fmt1(rr.men_vs_women.hdi[1])}×), an average gap of about <strong>${fmtN(pd.men_vs_women.median)} people per month</strong> (95% HDI ${fmtN(pd.men_vs_women.hdi[0])}–${fmtN(pd.men_vs_women.hdi[1])}). Across a wide range of reasonable priors, the posterior probability that the true mean monthly count of men exceeds women is indistinguishable from 1. </p> <div class="plain-eng"> <div class="pe-label">What it means</div> The model estimates a nearly 100% chance that the monthly count of men is higher than the count of women. The estimated count of men is about${fmt1(rr.men_vs_women.median)}× the count of women, or about${fmtN(pd.men_vs_women.median)} more people each month. <strong>Men are the largest group in the City's shelter count.</strong> </div> </div>`}
md`Men were at least **${_menShareMinPct}%** of the people counted as homeless in Toronto's shelter system in every month from ${_recordStartYear} to ${_recordEndYear}. In most months, their share was higher. On average, the count of men was about **${_gapAvgStr} people higher** than all other gender groups combined. That gap changed little over time.`
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.
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.