Metadata-Version: 2.4
Name: fars-toolkit
Version: 0.1.0
Summary: Download, filter, and aggregate NHTSA FARS fatal-crash data with zero non-stdlib dependencies.
Author: Marvin Bregiosa
License: MIT
Project-URL: Homepage, https://github.com/MarvinBregiosa/fars-toolkit
Project-URL: Repository, https://github.com/MarvinBregiosa/fars-toolkit
Project-URL: Documentation, https://github.com/MarvinBregiosa/fars-toolkit#readme
Project-URL: Issues, https://github.com/MarvinBregiosa/fars-toolkit/issues
Project-URL: Related Dataset (Zenodo), https://doi.org/10.5281/zenodo.20487070
Project-URL: Related Dataset (HuggingFace), https://huggingface.co/datasets/MarvinBregiosa/us-truck-fatalities-fars-2018-2024
Project-URL: Published Report, https://accidentlawyerreview.com/research/vision-zero-report-card/
Keywords: fars,nhtsa,traffic-safety,transportation,vision-zero,data-journalism,public-data,fatality-data,truck-safety,pedestrian-safety
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Sociology
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 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](https://www.nhtsa.gov/research-data/fatality-analysis-reporting-system-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](https://accidentlawyerreview.com/research/vision-zero-report-card/) — interactive 19-city report built on this exact pipeline.
- [Dataset on Zenodo (DOI)](https://doi.org/10.5281/zenodo.20487070) — the row-level CSV produced by this code.
- [Source repo on GitHub](https://github.com/MarvinBregiosa/vision-zero-fars).
- [HuggingFace mirror](https://huggingface.co/datasets/MarvinBregiosa/us-truck-fatalities-fars-2018-2024).

## Install

```bash
pip install fars-toolkit
```

Requires Python ≥ 3.9. No dependencies outside the standard library.

## Quick start

```python
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](https://accidentlawyerreview.com/research/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:

```python
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:

```bash
# 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](https://accidentlawyerreview.com/research/vision-zero-report-card/) — the interactive report built with this toolkit.
- [Build notes on dev.to](https://dev.to/marvin_bregiosa/building-a-sortable-data-journalism-page-in-astro-33k-fars-records-sparklines-and-the-bugs-that-55i) — the technical write-up that motivated this package.
- [On getting a DOI for indie data projects](https://accidental-lawyers.hashnode.dev/from-csv-to-citable-getting-a-doi-for-your-indie-data-project-and-why-every-indie-dev-with-public-data-should-bother) — how the companion dataset got onto Zenodo.
