Skip to contents

narcan turns raw US multiple-cause-of-death (MCOD) micro-data into drug- and opioid-overdose death counts and age-standardized rates. It implements the Injury Surveillance Workgroup (ISW7; Safe States Alliance) case definitions across both the ICD-9 (1979-1998) and ICD-10 (1999+) eras, and harmonizes the race, Hispanic-origin, and county recodes that NCHS changed over the years. This vignette maps the whole package; each step links to a dedicated vignette.

The pipeline

Most analyses follow the same four steps, from a raw NCHS file to a rate.

Step Functions Vignette
1. Import import_mcod_fwf(), unite_records() this vignette (below)
2. Flag flag_drug_deaths(), flag_opioid_deaths(), flag_opioid_types() vignette("classifying-overdose-deaths")
3. Denominators add_pop_counts() vignette("population-denominators")
4. Rates add_std_pop(), calc_stdrate_var() vignette("age-standardized-rates")

Further vignettes branch off this spine: vignette("demographic-recodes") (the year-aware age/sex/race recodes that produce the columns above), vignette("hispanic-origin") (Hispanic-origin stratification), and vignette("geography-fips") (sub-national geography). A measurement caveat – how the share of unspecified drug deaths shifts trends over time – is in vignette("unspecified-drug-deaths"). To see every step run on a real file, vignette("real-data-end-to-end") takes the public-use 2004 data all the way to an age-standardized opioid rate.

Getting the data

Public-use MCOD micro-data is distributed by NCHS and mirrored by NBER; the restricted All-County files require an NCHS data-use agreement (DUA). narcan can fetch a public-use year for you:

# Downloads the NBER public-use mirror into `download_dir`.
download_mcod_csv(2019, download_dir = "raw_data")   # or download_mcod_dta()

To read a raw fixed-width file (public or restricted), use import_mcod_fwf(), which applies narcan’s byte-verified column dictionary for the given year and tier:

raw <- import_mcod_fwf("mort2019us.dat", year = 2019, tier = "public")

The two tiers share the within-record layout; the public tier blanks sub-state geography (from 2005) and a few certifier-entered items, which import_mcod_fwf() returns as all-NA so public and restricted output are column-compatible.

No restricted-data agreement yet? The public-use file for 2004 is the last public year that still carries county geography (2005 onward suppress it). import_mcod_fwf() orders its output to the restricted column layout for every tier, so reading the 2004 public file already gives you a restricted-shaped frame with county of residence and occurrence – a DUA-free stand-in for developing county-level code before you obtain the restricted All-County files:

raw04 <- import_mcod_fwf("mort2004us.dat", year = 2004, tier = "public")

Two caveats. On the public file, county FIPS are populated only for counties with a 2000-Census population of at least 100,000; smaller (disproportionately rural) counties collapse to a residual code, so this stand-in exercises large-county behavior only. And on this file, passing tier = "restricted" recovers nothing extra: narcan declares no bytes at all for racer40 in 2004 (see ?mcod_fwf_dicts), and certifier, tobacco_use and pregnancy_status fall inside the record but are blank on the public file, so all four come back all-NA. Those three are populated in the genuine restricted 2004 file – the tier argument describes the layout you are reading, not the file you have.

From raw records to the flag pipeline

The flaggers key on two fields: the underlying cause (ucod) and a single string of all contributory cause codes (f_records_all). A real MCOD file stores the contributory codes across many separate record_* columns, so f_records_all is derived, not read directly – unite_records() builds it:

records <- unite_records(raw, year = 2019)

# `records` now carries `ucod` + the collapsed `f_records_all`, ready to flag:
flagged <- records |>
    flag_drug_deaths(year = 2019) |>
    flag_opioid_deaths(year = 2019)

From here you are at the start of vignette("classifying-overdose-deaths"), which walks the flag pipeline step by step. To run unite + the drug/opioid/type/intent flaggers in a single call, use the flag_all_deaths() convenience wrapper.

The columns narcan expects

Downstream functions read a handful of raw (or lightly recoded) MCOD columns. This is the minimal set; each vignette introduces the ones it needs.

Column Meaning Raw or derived
year data year (4-digit; 1979-1995 files use a 2-digit datayear) raw
ucod underlying cause of death (ICD code) raw
record_1, record_2, … contributory cause-code columns raw
f_records_all space-joined contributory codes derived (unite_records())
restatus residency status (1-3 = US resident) raw
age age (see remap_age() / categorize_age_5()) raw / recoded
sex sex (see categorize_sex()) raw / recoded
race race (see categorize_race(); era-dependent) raw / recoded
hspanicr Hispanic-origin recode (see categorize_hispanic_origin()) raw
countyrs / countyoc county of residence / occurrence raw

Next steps

Start with vignette("classifying-overdose-deaths") (the flag step), then vignette("population-denominators") and vignette("age-standardized-rates") to turn counts into rates. vignette("real-data-end-to-end") runs that whole path on a real public-use file. Branch to vignette("demographic-recodes") for the age/sex/race recoders, vignette("hispanic-origin") to stratify by Hispanic origin, or vignette("geography-fips") for sub-national work; vignette("unspecified-drug-deaths") covers a measurement caveat.

A note on this documentation

Every runnable chunk in these vignettes uses small synthetic or bundled data, never restricted records, so they build without a data-use agreement or a network connection. The download/import chunks above are shown with eval = FALSE because they need a real file; swap in your own path to run them.