BonQuery
  • Home
  • Dashboards
  • Left in the Cold
  • Data & Methods
    • About the Data
    • Daily Data Sources
    • Central Intake Validation
  • About
  • FR

YTD Comparison

Toronto Shelter System — year-to-date cumulative trends

Year-to-date (YTD) comparison of Toronto’s shelter system — see whether the YTD selected in the dropdown is tracking above or below prior years on the inflow/outflow categories: Newly Identified and Moved to Permanent 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()

// Month dropdown: display "March 2026", return "2026-03-01", default = most recent.
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: new Date(y, m - 1, 15).toLocaleString("default", {month: "long", year: "numeric"}),
      value: d.flow_date
    }
  })
viewof reportingDate = Inputs.select(reportingMonthOptions, {
  label: "Reporting Month",
  format: d => d.label,
  value: reportingMonthOptions[0]
})
reportingYear  = +reportingDate.value.slice(0, 4)
reportingMonth = +reportingDate.value.slice(5, 7)

// YTD cumulative sums for current year and prior year through selected month.
ytdComputed = {
  const rows = data
    .filter(d => d.population_group === "All Population")
    .filter(d => {
      const y = +d.flow_date.slice(0, 4)
      const m = +d.flow_date.slice(5, 7)
      return (y === reportingYear || y === reportingYear - 1) && m <= reportingMonth
    })
    .sort((a, b) => a.flow_date.localeCompare(b.flow_date))

  const accNI = {}, accMH = {}
  return rows.map(d => {
    const y = +d.flow_date.slice(0, 4)
    const m = +d.flow_date.slice(5, 7)
    accNI[y] = (accNI[y] || 0) + d.newly_identified
    accMH[y] = (accMH[y] || 0) + d.moved_to_housing
    return {
      year: y, month: m,
      monthLabel: MON[m - 1],
      yearLabel: String(y),
      isCurrentYear: y === reportingYear,
      ytd_ni: accNI[y],
      ytd_mh: accMH[y]
    }
  })
}

// Sort: by month, prior year first within each month — creates visual bar grouping
ytdSorted = ytdComputed
  .sort((a, b) => a.month - b.month || a.year - b.year)
  .map(d => ({...d, barLabel: `${d.monthLabel} ${d.yearLabel}`}))

ytdDomain = ytdSorted.map(d => d.barLabel)

Cumulative YTD comparison: Newly Identified

Plot.plot({
  width,
  height: 60 + ytdSorted.length * 22,
  marginLeft: 100,
  marginRight: 80,
  x: {label: "Cumulative newly identified (YTD)", grid: true, tickFormat: fmtN},
  y: {label: null, domain: ytdDomain},
  color: {
    domain: [String(reportingYear - 1), String(reportingYear)],
    range: ["#FFB3C1", "#FF2D55"],
    legend: true
  },
  marks: [
    Plot.ruleX([0]),
    Plot.barX(ytdSorted, {x: "ytd_ni", y: "barLabel", fill: "yearLabel", tip: true,
                          insetTop: 1, insetBottom: 1}),
    Plot.text(ytdSorted, {
      x: "ytd_ni", y: "barLabel",
      text: d => d.ytd_ni.toLocaleString(),
      textAnchor: "start", dx: 5, fontSize: 11
    })
  ]
})

Cumulative YTD comparison: Moved to Permanent Housing

Plot.plot({
  width,
  height: 60 + ytdSorted.length * 22,
  marginLeft: 100,
  marginRight: 80,
  x: {label: "Cumulative moved to permanent housing (YTD)", grid: true, tickFormat: fmtN},
  y: {label: null, domain: ytdDomain},
  color: {
    domain: [String(reportingYear - 1), String(reportingYear)],
    range: ["#AED5AE", "#5BA75B"],
    legend: true
  },
  marks: [
    Plot.ruleX([0]),
    Plot.barX(ytdSorted, {x: "ytd_mh", y: "barLabel", fill: "yearLabel", tip: true,
                          insetTop: 1, insetBottom: 1}),
    Plot.text(ytdSorted, {
      x: "ytd_mh", y: "barLabel",
      text: d => d.ytd_mh.toLocaleString(),
      textAnchor: "start", dx: 5, fontSize: 11
    })
  ]
})

Explore other dashboards:

  • Historical Trends
  • Monthly Snapshot

This dashboard is also available as an Oracle APEX dashboard built on the same database — useful if you want to see how the same data renders in Oracle’s native low-code platform, or if you’re curious about the SQL and APEX configuration behind it.

Key terms

Inflow categories (people entering the shelter system this month):

  • Newly Identified. People entering the shelter system for the first time. One exception for the “Chronic” group: in that row, this column counts people who became chronically homeless during the reporting month, regardless of how long they’d already been using the shelter system.

Outflow categories (people leaving the shelter system this month):

  • Moved to Permanent Housing. People who left the shelter system for permanent housing.

Definitions are based on the City of Toronto’s Shelter System Flow Data page.

Read all data findings →

Data source

All data comes from the City of Toronto’s Shelter System Flow dataset, published monthly on the City’s open data portal.

Contains information licensed under the Open Government Licence – Toronto.

Back to top
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
 

Independent public-interest analysis of Toronto’s shelter system.
© 2026 Miriam Marling · BonQuery