Skip to content

About

Download, filter, and aggregate NHTSA FARS fatal-crash data with zero non-stdlib dependencies. Companion to the Vision Zero Report Card.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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:

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-8 fails on 2018 files; reading with latin-1 leaves a BOM visible as  on 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_TYP is 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

About

Download, filter, and aggregate NHTSA FARS fatal-crash data with zero non-stdlib dependencies. Companion to the Vision Zero Report Card.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages