Download, filter, and aggregate NHTSA FARS fatal-crash data with zero non-stdlib dependencies.
Project description
fars-toolkit
Download, filter, and aggregate NHTSA FARS fatal-crash data with zero non-stdlib dependencies.
A small, focused Python library for indie data-journalism and research workflows. No pandas, no PyArrow, no Spark — just the standard library, the public NHTSA FARS CSV downloads, and a few dozen lines that handle the annoying parts (BOM bytes, varying folder layouts, the BODY_TYP join).
If you're doing serious modelling work, use pandas or polars. If you want to pull seven years of FARS, filter to crashes involving a specific vehicle class, and aggregate per-city or per-state, this is the lighter path.
Companion to the published research:
- Vision Zero Report Card 2026 — interactive 19-city report built on this exact pipeline.
- Dataset on Zenodo (DOI) — the row-level CSV produced by this code.
- Source repo on GitHub.
- HuggingFace mirror.
Install
pip install fars-toolkit
Requires Python ≥ 3.9. No dependencies outside the standard library.
Quick start
from fars_toolkit import (
download_year, load_year, truck_cases, by_city, by_state
)
# 1. Pull the 2024 zip (~32 MB) into ./data/ and extract.
download_year(2024, into='data/')
# 2. Locate the inner CSVs. Folder layout varies year to year; this handles
# the 2018-2024 vintages automatically.
accident, vehicle = load_year(2024, base='data/')
# 3. Find all crashes that involved a medium/heavy commercial truck.
# By default, body types 60-69 (the standard NHTSA truck definition).
cases = truck_cases(vehicle)
# 4. Aggregate fatalities by (state, city) FIPS code.
per_city = by_city(accident, cases)
print(per_city[(17, 1670)]) # truck-involved fatalities in Chicago, IL, 2024
# 5. Or aggregate by state.
per_state = by_state(accident, cases)
print(per_state[6]) # truck-involved fatalities in California, 2024
What it handles
NHTSA's FARS CSVs have a few rough edges that bit me when I built the Vision Zero Report Card and inspired this package:
- BOM bytes in 2021–2024 files. Reading naïvely with
utf-8fails on 2018 files; reading withlatin-1leaves a BOM visible ason the first column header, which silently breaks the dict key lookup.open_fars_csv()strips it transparently. - Folder layout varies. 2018–2019 zips extract their CSVs directly into the target folder. 2020+ wrap them in an inner
FARS{year}NationalCSV/subfolder.load_year()tries both. - Multi-table join. A "truck-involved crash" is defined on the vehicle table (one row per vehicle, where
BODY_TYPis in 60–69) but the fatality count lives on the accident table.truck_cases()+by_city()does that join cleanly without loading everything into memory.
Truck definition
By default, truck_cases() uses FARS BODY_TYP codes 60–69 — medium and heavy commercial trucks. This matches the truck-crash definition NHTSA and IIHS use in safety research; it excludes passenger pickups under 10,000 lb GVWR.
You can pass a custom set:
from fars_toolkit import truck_cases
# Only truck-tractors (semis):
semi_cases = truck_cases('vehicle.csv', body_types={68})
# Step vans and HAZMAT placards:
small_truck_cases = truck_cases('vehicle.csv', body_types={60, 61})
The package exposes BODY_TYP_LABELS if you want human-readable labels for each code.
CLI
The library ships a thin CLI for the common one-liners:
# Download a batch of years.
fars-toolkit download --year 2018 2019 2020 2021 2022 2023 2024 --into data/
# Top 20 cities by truck-involved fatalities in 2024.
fars-toolkit cities --year 2024 --base data/ --top 20
# Per-state truck-fatality totals for 2024.
fars-toolkit state-totals --year 2024 --base data/
API summary
| Function | What it does |
|---|---|
download_year(year, into=...) |
Fetch and extract a FARS National CSV zip. |
load_year(year, base=...) |
Locate the accident.csv + vehicle.csv paths inside an extracted folder. |
truck_cases(vehicle, body_types=...) |
Return the set of (state, st_case) crash IDs whose vehicle is a truck. |
by_city(accident, cases=...) |
Aggregate FATALS by (state, city) FIPS, optionally restricted to a case set. |
by_state(accident, cases=...) |
Aggregate FATALS by state FIPS. |
series_for_city(state, city, years, base=...) |
Build a year → fatalities series for one city across multiple years. |
open_fars_csv(path) |
Encoding-tolerant csv.DictReader opener, BOM-stripped. |
Caveats inherited from FARS
These are the caveats baked into the underlying NHTSA data; the package can't fix them, but you should know:
- City boundaries. FARS uses federal FIPS city codes. Crashes on through-routes at city limits sometimes attribute to the surrounding county instead of the city, so urban truck-deaths are likely undercounted for cities with major interstate-through-routes.
- 2024 is preliminary. The 2024 Annual Report File released April 2026 will be re-released as the Final File in 2027 and may revise counts slightly.
- Small annual counts are noisy. For cities with single-digit annual truck fatalities, year-over-year percentage swings reflect statistical noise as much as policy outcomes.
License
MIT.
Related
- Vision Zero Report Card 2026 — the interactive report built with this toolkit.
- Build notes on dev.to — the technical write-up that motivated this package.
- On getting a DOI for indie data projects — how the companion dataset got onto Zenodo.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fars_toolkit-0.1.0.tar.gz.
File metadata
- Download URL: fars_toolkit-0.1.0.tar.gz
- Upload date:
- Size: 12.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af87d42aba69687c492ebeb14141d90a3430f0b89b6f2238c957254104baed1e
|
|
| MD5 |
406f48196628c14af40cca02fad9e513
|
|
| BLAKE2b-256 |
b74090c1349a3a11314959ba1ebcd1713a246e1987760cd4325294b678a8d3c8
|
File details
Details for the file fars_toolkit-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fars_toolkit-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
649d2c299cca8e1ac7ee77b33028d901f5aff6298302e6406b4af9ab2825f0fb
|
|
| MD5 |
af5f5050d1b5cb7b96a0dbea65b54018
|
|
| BLAKE2b-256 |
1043c72dc4ebb7cb4c204308e5cbb03e9f7c89cd565c0ace08d2d57c2129974b
|