What this site shows

Each layer on the map displays a single variable for a single year, across all 50 U.S. states. The full underlying dataset is a balanced state-year panel covering 1979–2024 for the law and crime variables, with shorter coverage windows for variables whose source series start later (NICS background checks from 1999; ACS demographics from 2008; RAND household ownership 1980–2016; firearm suicide / homicide / FS ownership proxy 1979–2023). Year sliders gracefully gray out states without data in that year.

The site is generated from CSV/JSON files in the same git repository. The data pipeline is in scripts/build_website_data.py, which reads the project’s balanced panels and emits docs/data/panel.json and docs/data/metadata.json. To rebuild after a data update: python scripts/build_website_data.py.

Important caveats

Manipulations applied to the raw data

Variable familyWhat was doneWhy
Granular crime (homicide, robbery, ...) The OpenCrime feed double-listed North Carolina 2022 and omitted North Dakota 2022. The second NC 2022 row (population ≈ 779k) was reassigned to ND. Logged in data/processed/crime_repairs_log.csv. Applied identically in the website build to keep all granular crime variables balanced.
Pre-ACS demographic shares (1990–2007) Reconstructed annual non-Hispanic race shares, male share, and age-group shares from Census ASRH (1990–1999), the 2000s intercensal all-data file, and PEP (2010–2024). ACS 1-year tables are only available from 2008. The reconstruction matches Census published denominators within rounding.
Bachelor’s+ share before 2008 Linearly interpolated within state from Census Statistical Abstract Table 229 anchors (1990, 2000, 2008) and ACS 2008–2024 actuals. Single-year educational attainment estimates are not consistently available before ACS.
SAIPE income / poverty Missing 1990–1992 and 1994 values are linearly interpolated within state. SAIPE skipped those years; interpolation preserves a balanced panel.
Per-capita income, household income BEA per-capita personal income and SAIPE median household income are deflated to 2024 dollars using CPI-U. Real-terms comparability across the long panel.
Firearm suicide rates / total suicide rates Computed on the fly as firearm_suicides / total_population × 100000 (and similarly for total suicides) from the firearm-suicide-share dataset. Source file ships counts and the FS/S ratio but not population-normalized rates.
Law-category indices Derived as simple sums of the relevant Tufts binary indicators (background checks, carry restrictions, banned weapons, buyer restrictions, DV prohibitors, storage / GVRO). Group-level totals are easier to read on a map than 72 individual indicators. The category groupings are listed in scripts/build_website_data.py.

All variables — State view (1979–2024)

Variables are grouped by category. Click a source link to open the original data provider in a new tab.

Loading state variables...

All variables — County view (2009–2024)

The county view exposes a different (smaller) set of variables: county crime from Jacob Kaplan's UCR aggregations, county demographics from ACS 5-year, county economy (SAIPE income/poverty + ERS unemployment + BEA per-capita income), and state firearm laws plus state firearm mortality joined down to every county in a state-year.

Loading county variables...

Data sources cited on this site

How to reproduce

  1. Clone this repository.
  2. Install Python with pandas, numpy, and openpyxl.
  3. State panels: python scripts/build_firearms_panel.py (rebuilds the original data/processed/*.csv from raw inputs), then python scripts/audit_panels.py and python scripts/augment_panels.py.
  4. County panel (Phase 1+2a): python scripts/build_county_panel.py. Auto-fetches ACS 5-year via the Census API (cached) and reads PEP/SAIPE/ERS/BEA flat files from data/county/.
  5. County crime (Phase 3): manually download Jacob Kaplan's openICPSR project 100707 V22 and unzip into data/county/kaplan_offenses/, then run python scripts/build_county_crime.py. Re-run build_county_panel.py to merge the crime layer in.
  6. Website data files: python scripts/build_website_data.py (state) and python scripts/build_website_county_data.py (county per-year files).
  7. Preview: python -m http.server 8000 -d docs then open http://localhost:8000.

The full prose log of every data source and manipulation lives in data_appendix.md at the repository root.

Generated at