Automated speed enforcement, 2020–2025, and the year after

Toronto's Speed Cameras

790,725 collisions · 520 camera sites · 627 enforcement periods · 4,565 radar signs · 2 cities · 2014–2026

Every number on this site can be recomputed from these files, on another machine, with different software.

The kind of evidence this is. These are observational data from a programme that launched into a pandemic-era traffic collapse. We report associations, carefully tested, and state the uncertainty plainly. The camera-free-year readings are preliminary and sealed before the data existed; a confirmatory re-pull is due October 2026.

Download the data

Everything below is the data the published numbers are computed from, 12 files and about 20 MB in total. The collision, camera and ticket data are public records from the City of Toronto and the Toronto Police Service; nothing here identifies an individual.

site_explorer.csv520 rows × 18 cols · 70.1 KB
One tidy row per camera site with ticket volume joined on: rates before, during and after, injury counts, deployments, tickets per month and totals. The site explorer reads this file.
camera_effectiveness.csv520 rows × 25 cols · 73.1 KB
One row per camera site: crash and injury counts and monthly rates before, during and after enforcement, percent change, rebound, ward and coordinates. The headline within-site results are computed from this file.
enforcement_periods.csv627 rows × 13 cols · 788.9 KB
One row per enforcement period. Start and end dates define which collisions count as during enforcement at each site.
wys_effectiveness.csv4,565 rows × 14 cols · 488.5 KB
One row per Watch Your Speed radar sign deployment, on the same before/during/after construction as the cameras. Note the zero-crash deployments: a quarter were too short to record any crash.
collisions_flat.csv790,725 rows × 6 cols · 18.1 MB
One row per collision: date, year, injury flag, whether it was geocodable, whether it fell within 250 m of a camera, and whether that camera was enforcing at the time.
waterloo_annual_index.csv6 rows × 9 cols · 538 B
Annual collision counts and injury counts for Toronto and Waterloo Region, 2019 to 2024, indexed to 2019. Includes Waterloo's injury rate, which exposes the 2024 coding change.
definitive_rebound_primary.csv1 rows × 10 cols · 255 B
The time-matched departure rebound decomposition, flattened to one row. This is the adjusted rebound the report treats as primary.
police_backfill_panel.csv788 rows × 4 cols · 13.8 KB
Neighbourhood-by-period panel testing whether police speeding tickets tracked camera coverage across the 627 rotations.
reporting_bounds.csv520 rows × 4 cols · 30.9 KB
Bounds on how much reporting-practice drift could shift the injury and property-damage estimates, per site-month.
site_vision_zero_coding.csv520 rows × 8 cols · 32.4 KB
Per-site coding of overlapping Vision Zero and physical traffic-calming treatments, used to test co-deployment confounding.
Site-level results for the camera-free year: crash and injury excess against control areas, by window, with the simulated-null calibration.
Base R, no packages required. Recomputes the study's headline numbers from the CSVs beside it and prints an OK or FAIL per check.

Reproduce the headline numbers

replication.R uses base R only, with nothing to install, and reads the plain CSVs beside it. It prints an OK or FAIL for each check. The study's credibility rests on a simple promise: every published number can be recomputed from the archived data by someone else, on another machine, with different software.

Data & code

A real R session, running in your browser through webR. Nothing is sent anywhere. The camera table is already loaded as sites, one row per camera site. The first run downloads R itself, about 30 MB.

Press Run to start R.
# download the bundle, then:
$ Rscript replication.R

# or read a single table straight from the site
cam <- read.csv("https://toronto-speed-cameras.pages.dev/data/camera_effectiveness.csv")
nrow(cam)                      # 520 camera sites
median(cam$pct_change, na.rm=TRUE)   # about -10.2

# the departure rebound, on the report's 383-site sample
s <- subset(cam, after_rate > 0 & during_rate > 0 & after_months >= 3)
nrow(s)                                   # 383
mean(s$during_rate); mean(s$after_rate)   # 2.20 ; 2.53
mean(s$after_rate > s$during_rate)        # 0.62
wilcox.test(s$after_rate, s$during_rate, paired=TRUE)

For counts and medians, OK means an exact match within rounding. For p-values, OK means the recomputed and published values land on the same side of the conventional 0.05 threshold; exact p-values can differ slightly because the archived data cache was refreshed in July 2026, which shifted a few sample sizes by one or two sites without changing any conclusion. An OK verdict says the data produce the published number. It does not, by itself, say cameras caused anything.

Columns in the collision file

ColumnMeaning
dateCollision date
yearCalendar year, 2014 through 2026
injuryWhether at least one person was injured (the TPS any-severity personal-injury flag, not the KSI file)
geocodedWhether the record carries usable coordinates; about 84% do
near_cameraWithin 250 m of any of the 520 camera sites
treatedWithin 250 m of a camera while that camera was enforcing

Spatial operations were performed in EPSG:32617 (UTM Zone 17N, metres). The 16 percent of records without usable coordinates are excluded from the spatial analysis and included in citywide trends; they differ modestly from the geocodable records, skewing less severe.