Every incident MMDA posted between 2018-08-20 and 2020-12-27. Half of them are in one city and 146 days in the span have no report at all — so this measures a Twitter account, not a road network. What it does capture is 15 March 2020, when the daily rate fell from 28.24 to 3.12.
This is a record of what MMDA tweeted, and the distribution proves it: 50.4% of all 17,312 incidents are in Quezon City, 91.59% are in four cities, and Navotas has 3 in two and a half years.
Incidents by city, after repairing the encoding. This is not a ranking of dangerous places — it is a ranking of which MMDA units post to Twitter, and it is the single most important thing to know before reading anything else here.
8,725 of 17,312 incidents. Four cities carry 91.59% between them.
Metro Manila has 17 local government units; 3 never appear in the file. The published figure of 15 counted Parañaque twice.
Incidents in two and a half years. Not a safe city — an unreported one.
Metro Manila entered enhanced community quarantine on 15 March 2020 and stayed under ECQ or MECQ until 31 May. Rates are per reporting day, not per calendar day, because the account goes quiet for stretches and dividing by calendar days would blame the roads for a silent feed.
2018-08-20 to 14 March 2020.
A fall of 89.0%. February had 775 incidents; April had 11.
Through to 2020-12-27 — still 62.0% below the pre-lockdown rate seven months later.
Incidents by hour, across the 17,174 rows with a time that parses. Two things stand out: the morning peak is larger than the evening one, and 1pm is a deeper trough than any hour either side of it.
1,354 incidents, 7.88% of all timed reports.
06:00–09:59 against 16:00–19:59, a gap of 8.16 points.
Against 1,354 at the 7am peak — 41.2% of it. The quietest hour is 2:00 with 57.
Incidents per reporting day by weekday. Dividing by reporting days rather than totals matters here: Sunday has fewer reporting days as well as fewer incidents, and a raw total would confuse the two.
The busiest weekday.
47.3% of Tuesday. The weekday-to-weekend difference is the clearest ordinary pattern in the file.
Days inside the 861-day span with no report at all. Metro Manila did not have 146 incident-free days.
The type column is free text, not a code list: 478 distinct strings across 17,312 rows, including at least five misspellings of “vehicular”. Grouping by substring rather than by exact match is why the family totals differ from the published figure of 11,781, which matched neither.
11,818 incidents across 26 distinct spellings.
3,956 incidents across 276 spellings — mostly “STALLED <vehicle> DUE TO MECHANICAL PROBLEM” with the vehicle changing.
Grouped into 7 families by what the string says, so a new vehicle in a later vintage lands in the right family rather than falling out. The families also catch protests and DPWH road works, which MMDA reports as traffic incidents.
The most-reported locations, all of them on two roads. EDSA alone accounts for nearly half of every located incident, which is partly about EDSA and partly about where MMDA has cameras and enforcers.
In Mandaluyong. The single most-reported spot in the file.
8,306 incidents on one road, out of every row that names a location.
Free text again, so “EDSA GUADALUPE” and “EDSA GUADALUPE NB” are two entries. The count is an upper bound on distinct places.
WHO estimates of road traffic deaths per 100,000, for the Philippines and five ASEAN neighbours. These are national, annual and comparable — everything the tweets are not. They are also modelled rather than counted: WHO adjusts national registrations for known under-reporting.
Deaths per 100,000. 3rd lowest of 6.
Since 2000, when it was 9.8. Only 2 of the six moved that way; the other is Vietnam.
The highest of the six, and falling. A low rate that is rising and a high rate that is falling are different problems.
Four limits, all of them structural rather than fixable.
It is a count of tweets. 146 of the 861 days in the span have no report at all and 2 whole months are empty, which means the series measures reporting activity and traffic together with no way to separate them. Every rate on this page is per reporting day for that reason.
The tweets say a vehicular accident occurred and how many lanes were blocked. They do not say whether anyone was hurt. A check asserts the injury and fatality counts stay at zero so a later column cannot quietly turn an incident count into a casualty figure.
An incident count without traffic volume is not a rate. Quezon City has more reports than Navotas and also vastly more road, more vehicles and more MMDA presence; nothing here separates those. The national death rates in section 7 are per capita, which is a denominator, and they rank the cities differently than the tweet counts would.
Which means the recovery from lockdown is only partly visible: the rate was still 62.0% below its pre-March level when the data ends, and whether it returned is outside this file.
One fetcher, ten CSVs, and a coverage table that counts every fault rather than silently repairing it.
“Parañaque” is “Parañaque” whose UTF-8 bytes were decoded as Latin-1. Re-encoding to Latin-1 and decoding as UTF-8 reverses it exactly, and is applied only when the marker byte is present so a correct string is never touched. A check then fails if any double-encoded name survives, and another asserts Parañaque is one row rather than two.
The 55 rows at exactly 0,0 are real incidents with a broken geocode. Dropping them would understate the counts; keeping the coordinate would put them in the Gulf of Guinea. The coordinate is cleared, the incident still counts, and a check asserts every surviving coordinate is inside a Metro Manila bounding box.
The file uses “7:55 AM”, “3:20PM” and bare “7:55”. One row reads “22:55 PM” — a 24-hour clock wearing a 12-hour marker — which a naive conversion turns into hour 34. That is how it was found. Anything that does not match an anchored pattern is recorded as unparseable rather than guessed at.
The monthly table is built across the whole calendar span rather than over the months that happen to have data, so July and August 2020 appear as zero rows carrying a note. A check fails if a zero month has no note, because a chart that plots a reporting gap as zero is asserting something false.
478 distinct free-text strings, including VEHCICULAR, VEHICUKAR and VEHICHULAR ACCIDENT. Grouping on what the string contains rather than matching a hand-kept list means the misspellings land in the right family and a new vehicle in a later vintage does not fall out. A warn-level check lists the “vehicular” variants that still sort into “other”.
WHO Global Health Observatory road death estimates, fetched from the World Bank WDI API because it is reachable and returns a clean error on a wrong indicator code rather than an empty success.
Every figure on this page traces to one of these, through a CSV in
data/ph-traffic/. Each is checked against its source query on every
build.
17,312 incident reports scraped from the MMDA Twitter account, 20 August 2018 to 27 December 2020: date, time, city, location, coordinates, incident type, direction, lanes blocked, and the tweet text — A record of what MMDA tweeted, not a register of what happened. Quezon City alone is 50.4% of the file, 55 rows are geocoded to 0,0, and July and August 2020 contain no rows at all.
Estimated road traffic deaths per 100,000 population, 2000-2019, for the Philippines and five ASEAN neighbours — The national counterweight. Annual, comparable across countries, and modelled rather than counted -- WHO adjusts national registrations for known under-reporting.
The origin of every incident row; each row carries the status URL it came from — Cited for provenance. The tweets themselves were not re-fetched, and many of the linked statuses are no longer retrievable.
If you have raw data and want to transform it into meaningful insights like this analysis, I'd love to help! Whether it's business data, research datasets, or any information you want to visualize and understand better.
Have a dataset you'd like analyzed or need a mini AI project as a starter guide? Send me your suggestions!
I wrote a companion post — “A City Got Counted Twice, And 55 Crashes Landed In The Atlantic” — in simple, everyday words.