Skip to main content

Python Versions PyPI License Documentation Status

NHANES parser

Python parser and scraper for NHANES accelerometry and questionnaire

https://wwwn.cdc.gov/nchs/nhanes/default.aspx

Disclaimer

pyNHANES is an independent project. It is not affiliated with, endorsed by or reviewed by the CDC, the National Center for Health Statistics, or any other body responsible for NHANES.

It is published as is, with no warranty of correctness, completeness or fitness for any purpose, and certainly may contain bugs. NHANES is a large and irregular dataset, and every decision this package makes on your behalf - which values become missing, how a variable is merged across surveys, how a derived column is computed - is a judgement that may not suit your question. Those decisions are written down in How the data were processed so that you can check them.

Treat pyNHANES as a fast way to screen a large dataset and find out what is worth pursuing, not as a source of final numbers. Before anything is published, it is strongly recommended to verify the variables you rely on against the NHANES documentation, and reproduce the result with your own implementation of the steps that matter.

Quick start

The default path uses data that is already prepared, so nothing is downloaded from NHANES. The codebook of every variable ships inside the package, and the parsed tables are published as a GitHub release. Both are snapshots taken at release time, which is what makes the first look at NHANES take seconds instead of spending a whole evening.

pip install pynhanes
pynhanes-scraper -o CSV/nhanes_codebook.csv     # the shipped codebook, ~1 second

Reach for --refresh when the snapshot is not enough - when NHANES has published a survey since this release, or when you want variables that are not in the curated list, or values that are not recoded. Then the full pipeline below runs for real: the documentation is scraped, the data files are downloaded, and the table is parsed on your machine.

The four notebooks in [scripts/](https://github.com/timpyrkov/pynhanes/tree/master/scripts) walk through both precalc and true scrape/download/parse paths, and are also the Examples in the documentation.

Yes/No is recoded to 1/0, not 1/2

Worth knowing before you compare a parsed user data table with a NHANES documentation page. NHANES encodes Yes as 1 and No as 2. Other biobanks - the UK Biobank among them - often encode it differently, Yes as 1 and No as 0, and the same analysis run over both would be silently wrong. pyNHANES therefore rewrites values to more intuitive notation of No to 0, so that a column of answers can be averaged and compared.

The codebook carries both dictionaries: Codebook is what NHANES publishes, Recoded Codebook is what the default parsed table holds. You may require to keep the original codes with --no-recode, or recode=False in pynhanes.parser.parse().

Features

  • Scrape the NHANES documentation into a codebook - what every variable means and what its values mean
  • Parse .xpt and mortality .dat files into one Pandas table, all surveys stacked
  • Parse minute-level accelerometry of 2003-2006 and 2011-2014 into NumPy arrays

The pipeline

NHANES is organised as surveys (2011-2012) -> components (Questionnaire) -> data files (OCQ) -> variables (OCD150). pynhanes walks that in four steps, and each step is one command:

  1. pynhanes-scraper     what NHANES has          free, reads nothing from the website
  2. pynhanes-downloader  get the data files       ~0.8 GB with the default "core" preset
  3. pynhanes-parser      one table of everything  CSV/nhanes_userdata.csv
  4. pynhanes-activity    minute-level movement    optional, needs ~18 GB of raw files
     -> pynhanes.NhanesLoader reads it all back for analysis

Start with the scraper. It writes the codebook of every NHANES variable, and it costs nothing - read it before downloading anything, because it is the overview of what NHANES has. --availability and --datafiles add the per-answer counts and the catalogue of data files.

pynhanes-scraper -o CSV/nhanes_codebook.csv --availability --datafiles

Then download. A bare run takes only the data files the parser uses - the core preset, about 0.8 GB for all surveys. -n shows the plan and downloads nothing.

pynhanes-downloader -n -o XPT          # plan with sizes
pynhanes-downloader -o XPT             # core preset, ~0.8 GB

Then parse. One row per participant, one column per variable name, all surveys stacked. With no -v it uses the 341 variables curated in pynhanes/data/nhanes_variables.json, which ships with the package.

pynhanes-parser -i XPT -o CSV/nhanes_userdata.csv

Accelerometry is optional and large: download it explicitly, then parse it into .npz.

pynhanes-downloader -o XPT -a 2003,2005,2011,2013      # ~18 GB on disk
pynhanes-activity -i XPT -o NPZ -c CSV/nhanes_activity.csv

Then analyse. pynhanes.NhanesLoader reads the table back, attaches the accelerometry from NPZ/, turns numeric answers into labels with the codebook, and encode() spreads categorical answers into one column per answer. See scripts/load_and_plot.ipynb.

The four notebooks in scripts/ do the same as the four commands, if you would rather work in Jupyter. Get them with:

wget https://github.com/timpyrkov/pynhanes/archive/master/scripts.zip
unzip -j scripts.zip 'pynhanes-master/scripts/*' -d workfolder
cd workfolder; mkdir XPT NPZ CSV

A word of warning. NHANES has recoded fields since 1999. pynhanes merges a variable across surveys into one row, taking the newest label and the union of value codes - which is usually what you want, and sometimes not. Check the categorical variables you rely on against the NHANES website. How the data were processed says exactly what the merge does.

Command line scripts

command what it does
pynhanes-scraper scrape the variable codebook into a .csv
pynhanes-downloader download data files (.xpt, zipped .xpt, mortality .dat)
pynhanes-parser parse the data files into one table
pynhanes-activity parse the minute-level accelerometry into .npz arrays
pynhanes-wgetxpt the old one-file-at-a-time downloader, now a wrapper (also pywgetxpt)

Each is also python -m pynhanes.scraper, and usable from Python as pynhanes.scraper and so on. They all take -n/--noload to see what would happen without doing it, -j/--json for machine-readable output, and describe themselves for AI assistants in -h.

Every .csv pynhanes writes is ; separated, because variable names and value labels contain commas. Read one back with pd.read_csv(path, sep=";", index_col=0, header=[0, 1]).

The codebook

pynhanes-scraper writes up to three files. The package carries all three gzipped (1.8 MB in the wheel), so the default reads nothing from the website; --refresh, or picking a survey with -s, goes and looks (~5 minutes for 1603 pages).

file one row per holds
nhanes_codebook.csv variable 12774 variables of 385 data files, their labels and value codes
nhanes_availability.csv variable and survey 50873 rows: who was asked, and the count of every single answer
nhanes_datafiles.csv data file 482 data files, the surveys they span and their size

Besides the value labels, every variable carries:

column meaning
Type Continuous (6978), Binary (2464), Categorical (2106), Flag (933, present or missing), Text (270), Unknown (23)
Truncated top (159), bottom (106) or both (28): an end of the measurement is replaced by one value - everybody over 80 is reported as 80. The parser uses it as an ordinary number, but the tail is not real
Core the variable is in one of the 50 data files the parser uses - exactly what pynhanes-downloader fetches by default
Codebook value labels as NHANES publishes them: {"1": "Yes", "2": "No", "7": "Refused", "9": "Don't know"}
Recoded Codebook value labels after parsing: {"1": "Yes", "0": "No"} - No is 0 and the missing answers are gone. Comparing the two is how you tell whether a variable is recoded
Surveys, Availability, one column per survey in how many surveys it exists, how many answered in all, and how many in each
import pynhanes
pynhanes.scraper.snapshot_info()          # {'scraped': '2026-09-23', 'variables': 12774, ...}
book = pynhanes.scraper.read_snapshot()   # the codebook as a DataFrame, no file needed

Useful options: --availability and --datafiles write the other two files, -s/-c/-d narrow it to a survey, component or data file, --store keeps the pages in ~/.cache/pynhanes for a re-run. Full help in pynhanes-scraper -h.

Downloading data

pynhanes-downloader reads the NHANES website, skips files already present, checks free disk space, resumes interrupted downloads and never deletes anything.

option meaning
-o, --output destination folder, created if missing (default ./XPT)
-s, --surveys survey(s) by first year: 1999 ... 2017, 2021, or prepandemic (default: all)
-c, --component Demographics, Dietary, Examination, Laboratory, Questionnaire, Mortality
-d, --data-file default core - the 50 data files the parser uses (~0.8 GB). full - everything NHANES publishes (~4.5 GB); existing - codes already in your folder; @path - your own list; or codes such as DEMO,BMX
-a, --activity minute-level accelerometry: 2003, 2005 (PAXRAW), 2011, 2013 (PAXMIN). Nothing by default
--yes re-download selected files even if present
pynhanes-downloader -o XPT -d full                      # everything NHANES publishes
pynhanes-downloader -o XPT -s 2021 -c Laboratory
pynhanes-downloader -o XPT -d DEMO,BMX,SMQ              # selected data files, all surveys
pynhanes-downloader -o XPT -d existing                  # fill missing surveys of what you have
pynhanes-downloader -o XPT -s 2021 --yes                # re-download the latest survey

The core preset keeps data files that have one row per participant, cover the whole examined sample, are data rather than look-up tables, and exist across several surveys. NHANES publishes much more: 482 data file codes in all, of which only 26 appear in every survey and 41% in just one.

GB
core - what the parser uses 0.77
everything except accelerometry 4.47
accelerometry (PAXRAW 2003-2006, PAXMIN 2011-2014) 17.4

Sizes come from the NHANES listings and the .zip directories, so they are exact - within 0.4% of real downloads. Requests are polite: one per 0.2 s per server, with backoff on 429/5xx. Listings are cached in ~/.cache/pynhanes/, finished surveys once and the latest survey daily, so a repeat run normally sends no requests at all. PAXMIN is served by the slow ftp.cdc.gov; the plan measures the speed and warns. NHANES keeps adding data to recent surveys, so --yes is worth using for the latest one - it has to be narrowed with -s or -d.

Parsing

pynhanes-parser turns the downloaded files into one table - one row per participant, one column per variable, all surveys stacked (113249 participants, 348 variables with the shipped list).

option meaning
-i, --input folder with .xpt and mortality .dat files (default XPT)
-o, --output output .csv (default CSV/nhanes_userdata.csv)
-b, --codebook codebook of pynhanes-scraper; falls back to the shipped one
-v, --variables .json of variable names, or a comma-separated list; falls back to the shipped one
-l, --layout names - one column per variable name, (topic, name), codes merged and derived variables computed (default); codes - one column per NHANES variable code, nothing merged or derived
--write-variables write the resolved name -> codes mapping for you to curate
--prepandemic read the 2017-March 2020 files (P_*) instead of the 2017-2018 ones - the same people, renumbered
--no-derived, --no-recode skip derived variables / keep Yes/No as 1/2

The variables file is where the curation lives: one human-readable name, the NHANES codes it is made of, newest first. This matters because NHANES renames things (INDFMINC became INDFMIN2) and because some questions belong together (SMQ020, SMQ120, SMQ150 are cigarettes, a pipe and cigars). --write-variables writes a template from the codebook with the ambiguous names marked for review - a name that means more than one thing is refused rather than guessed, since Gender is both the participant and the household reference person.

The table says how it was made. The first cell of the first header row - which pandas leaves empty - carries a provenance line, so a table that has been copied away from its folder still says what it holds:

pynhanes 1.0.0 | names | recoded | derived | 2026-09-24;Demographic;Demographic
;Age;Gender
SEQN;;

Reading it the documented way is unaffected; the line comes back as table.columns.names[0], or from pynhanes.parser.read_provenance(table).

How the data were processed describes every step - which values become empty, how Yes/No becomes 1/0, and every derived variable - in a form meant to be quoted in a Materials and Methods section.

Accelerometry

Minute-level movement exists for two eras only: PAXRAW in 2003-2006 (a hip monitor, intensity counts, plus steps in 2005-2006) and PAXMIN in 2011-2014 (a wrist monitor, MIMS triaxial units, ambient light and a wake/sleep/non-wear label). pynhanes-activity reads them in chunks - about 1.5 GB of memory, ~40 s for 2003-2006 and ~2 min for 2011-2014 - and writes:

file holds
NPZ/nhanes_counts.npz activity counts, 2003-2006, 7 days
NPZ/nhanes_steps.npz step counts, 2005-2006, 7 days
NPZ/nhanes_triax_full.npz triaxial, light and status, 2011-2014, 9 days
NPZ/nhanes_triax.npz the best 7 of those 9 days
CSV/nhanes_activity.csv one row per participant: how many days hold usable data

Every array is one row per participant and one column per minute, and column 0 is midnight of the first day of wear. The 7-day arrays are rolled so that column 0 is Monday 00:00, and every array of one participant is rolled by the same number of minutes. The 9-day array is left in calendar order, because a roll wraps the end back to the front and would put the 8th and 9th day before the 1st.

status says what each minute was - 0 Missing, 1 Wake wear, 2 Sleep wear, 3 Non-wear, 4 Unknown - and each file carries the same list under Status keys; pynhanes.activity.decode_status() reads it back. Only 2011-2014 has the wake/sleep/non-wear prediction; for 2003-2006 the status is 0 or 4, which is how a minute with no data is told apart from a minute of perfect stillness.

pynhanes parses only the minute-level files. The hourly (PAXHR) and daily (PAXDAY) aggregates NHANES also publishes are not read as data - PAXDAY is used only for the start time of the first day, and PAXHD only for the monitor status.

Release files for pynhanes 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pynhanes 1.0.0
File Size Uploaded
pynhanes-1.0.0.tar.gz 1.9 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for pynhanes 1.0.0
File Interpreter ABI Platform
pynhanes-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 3.7 MB

Release files / pynhanes-1.0.0.tar.gz

Download URL pynhanes-1.0.0.tar.gz
Size 1.9 MB
Tags Source
SHA-256 checksum
How to use checksums
d17f4baef602d784787995423b929a2c60c563a8a13d68ac6331c7bf3d1ef6cc
BLAKE2b-256 checksum
How to use checksums
4904dbfdf64160e6581f3d5bebf123b900e4d77a6ec3c3ccba45162491e05d8e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.11

Release files / pynhanes-1.0.0-py3-none-any.whl

Download URL pynhanes-1.0.0-py3-none-any.whl
Size 1.8 MB
Tags Python 3
SHA-256 checksum
How to use checksums
f77b1462221c0048b912cc86a206f6e8b52a3ca35c995ff737181662fe66a82e
BLAKE2b-256 checksum
How to use checksums
b14c93bc51cf223ee0a19aa24db1764eabaa9eec37db67c1a15e8e7b92db8817
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.11

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.0.21

1 release file

0.0.20

1 release file

0.0.19

1 release file

0.0.18

1 release file

0.0.17

1 release file

0.0.16

1 release file

0.0.15

1 release file

0.0.14

1 release file

0.0.12

1 release file

0.0.11

1 release file

0.0.10

1 release file

0.0.9

1 release file

0.0.8

1 release file

0.0.7

1 release file

0.0.6

1 release file

0.0.5

1 release file

0.0.4

1 release file

0.0.3

1 release file

0.0.2

1 release file

0.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page