YouCele/nhanes-chd-classification

NHANES coronary heart disease classifier . ROC-AUC 0.88, PR-AUC 0.24. Built around a leakage investigation, a locked test set, and honest calibration, not a leaderboard score.

Python

1

0 commits

updated Sep 18, 2026

See the code

See what people are saying (1)

SourceMessageScoreDate

Classifying coronary heart disease risk from NHANES survey data (2011-2018), with a full leakage audit and calibration check [P] (r/MachineLearning)

Github repo : https://github.com/YouCele/nhanes-chd-classification I built a project that predicts self-reported, physician-diagnosed coronary heart disease using four cycles of NHANES data (2011-2012 to 2017-2018), about 21,500 adults after cleaning. It compares logistic regression with random…

1

Sep 18, 2026

README

Coronary heart disease classification with NHANES

A portfolio project: can routinely collected demographic, examination, laboratory and questionnaire data separate adults who report a physician-diagnosed coronary heart disease (CHD) from adults who do not?

Four NHANES cycles (2011-2012 through 2017-2018), self-reported outcome, binary classification, logistic regression against random forest and gradient boosting.

This is not a diagnostic tool. The outcome is a self-report, not a clinical or angiography-confirmed diagnosis, and the project makes no causal or prognostic claims. See reports/final_report.md for the full write-up and reports/research_decision_log.md for the reasoning behind every methodological choice.

Headline result

21,570 adults, 881 with the outcome (4.08%). On a held-out quarter of the cohort, never touched before the final evaluation:

metricvalue95% CI
ROC-AUC0.8750.854 - 0.895
PR-AUC0.2390.205 - 0.291

Age alone gets to a ROC-AUC of 0.83; the full 34-variable model reaches 0.87. The rest of the discrimination comes mostly from blood pressure, lipids and body size - the variables clinical guidance already uses, not anything new. Full numbers, including the leakage check, the calibration curve and the subgroup breakdown, are in outputs/ and in the report.

What makes this more than a Kaggle notebook

  • A documented leakage investigation. The MCQ160 cardiovascular questionnaire block (heart attack, angina, heart failure, stroke) is excluded from the model with a written reason for each variable, and the size of the effect is quantified rather than just asserted: adding that block back in moves PR-AUC from 0.23 to 0.51. Reporting the higher number would have been the easy, wrong thing to do.
  • No global preprocessing. Every imputation and scaling step lives inside the model pipeline, fitted per cross-validation fold. Nothing is computed on the full data set before the split.
  • A frozen threshold. The operating point is chosen on cross-validated development predictions before the test set is touched.
  • Recalibration, reported honestly. The class-weighted model's raw probabilities are badly miscalibrated (mean predicted risk 30% against an observed 4%). A sigmoid recalibration fixes this (slope 1.03, Brier 0.147 -> 0.034), and the report says plainly that the raw numbers should never be read as risks.
  • 51 automated checks against the saved outputs (scripts/run_07_audit.py, reports/audit_report.md), covering data integrity, target coding, leakage decisions, missingness handling, split design and reproducibility.
  • A report generated from its own numbers. reports/final_report.md is produced by scripts/run_08_report.py directly from the files in outputs/; nothing in it is typed in separately from the analysis.

What this project does not have

Smoking, diabetes and blood-pressure-medication data are not part of the local file set (no SMQ, DIQ, BPQ, GHB or GLU), so three of the best-established CHD risk factors are missing from the model. This is stated as a limitation, not worked around with a proxy variable.

Project structure

.
├── data/
│   ├── raw/            NHANES XPT files (not included, see data/raw/README.md)
│   └── processed/      intermediate cohort and split files, built by scripts/run_01
├── src/                the actual analysis code, one module per concern
├── scripts/            eight numbered scripts, one per group of phases, plus run_all.py
├── notebooks/          thin notebooks that run the scripts and display the results
├── outputs/            every table the analysis produces, as CSV or JSON
├── figures/            every plot, saved as PNG
├── models/             fitted pipelines (not tracked in git, rebuilt by script 4)
├── reports/            the final report, the decision log, and the audit report
├── HOW_TO_RUN.md        execution order and a map of every output file
└── requirements.txt

Running it

pip install -r requirements.txt
# put the NHANES XPT files under data/raw/, see data/raw/README.md
python scripts/run_all.py

See HOW_TO_RUN.md for the full execution order, what each step depends on, and a complete map of which file holds which result.

Data source

NHANES is public. Download it from the CDC (https://wwwn.cdc.gov/nchs/nhanes/); data/raw/README.md lists the exact files for each cycle. Raw XPT files are not committed to this repository.

License

Code is MIT licensed (see LICENSE). NHANES data is a public-domain product of the U.S. National Center for Health Statistics and is not covered by that license - it is not redistributed here in any case, see above.

YouCele/nhanes-chd-classification

NHANES coronary heart disease classifier . ROC-AUC 0.88, PR-AUC 0.24. Built around a leakage investigation, a locked test set, and honest calibration, not a leaderboard score.

Python

1

0 commits

updated Sep 18, 2026

See the code

See what people are saying (1)

SourceMessageScoreDate

Classifying coronary heart disease risk from NHANES survey data (2011-2018), with a full leakage audit and calibration check [P] (r/MachineLearning)

Github repo : https://github.com/YouCele/nhanes-chd-classification I built a project that predicts self-reported, physician-diagnosed coronary heart disease using four cycles of NHANES data (2011-2012 to 2017-2018), about 21,500 adults after cleaning. It compares logistic regression with random…

1

Sep 18, 2026

README

Coronary heart disease classification with NHANES

A portfolio project: can routinely collected demographic, examination, laboratory and questionnaire data separate adults who report a physician-diagnosed coronary heart disease (CHD) from adults who do not?

Four NHANES cycles (2011-2012 through 2017-2018), self-reported outcome, binary classification, logistic regression against random forest and gradient boosting.

This is not a diagnostic tool. The outcome is a self-report, not a clinical or angiography-confirmed diagnosis, and the project makes no causal or prognostic claims. See reports/final_report.md for the full write-up and reports/research_decision_log.md for the reasoning behind every methodological choice.

Headline result

21,570 adults, 881 with the outcome (4.08%). On a held-out quarter of the cohort, never touched before the final evaluation:

metricvalue95% CI
ROC-AUC0.8750.854 - 0.895
PR-AUC0.2390.205 - 0.291

Age alone gets to a ROC-AUC of 0.83; the full 34-variable model reaches 0.87. The rest of the discrimination comes mostly from blood pressure, lipids and body size - the variables clinical guidance already uses, not anything new. Full numbers, including the leakage check, the calibration curve and the subgroup breakdown, are in outputs/ and in the report.

What makes this more than a Kaggle notebook

  • A documented leakage investigation. The MCQ160 cardiovascular questionnaire block (heart attack, angina, heart failure, stroke) is excluded from the model with a written reason for each variable, and the size of the effect is quantified rather than just asserted: adding that block back in moves PR-AUC from 0.23 to 0.51. Reporting the higher number would have been the easy, wrong thing to do.
  • No global preprocessing. Every imputation and scaling step lives inside the model pipeline, fitted per cross-validation fold. Nothing is computed on the full data set before the split.
  • A frozen threshold. The operating point is chosen on cross-validated development predictions before the test set is touched.
  • Recalibration, reported honestly. The class-weighted model's raw probabilities are badly miscalibrated (mean predicted risk 30% against an observed 4%). A sigmoid recalibration fixes this (slope 1.03, Brier 0.147 -> 0.034), and the report says plainly that the raw numbers should never be read as risks.
  • 51 automated checks against the saved outputs (scripts/run_07_audit.py, reports/audit_report.md), covering data integrity, target coding, leakage decisions, missingness handling, split design and reproducibility.
  • A report generated from its own numbers. reports/final_report.md is produced by scripts/run_08_report.py directly from the files in outputs/; nothing in it is typed in separately from the analysis.

What this project does not have

Smoking, diabetes and blood-pressure-medication data are not part of the local file set (no SMQ, DIQ, BPQ, GHB or GLU), so three of the best-established CHD risk factors are missing from the model. This is stated as a limitation, not worked around with a proxy variable.

Project structure

.
├── data/
│   ├── raw/            NHANES XPT files (not included, see data/raw/README.md)
│   └── processed/      intermediate cohort and split files, built by scripts/run_01
├── src/                the actual analysis code, one module per concern
├── scripts/            eight numbered scripts, one per group of phases, plus run_all.py
├── notebooks/          thin notebooks that run the scripts and display the results
├── outputs/            every table the analysis produces, as CSV or JSON
├── figures/            every plot, saved as PNG
├── models/             fitted pipelines (not tracked in git, rebuilt by script 4)
├── reports/            the final report, the decision log, and the audit report
├── HOW_TO_RUN.md        execution order and a map of every output file
└── requirements.txt

Running it

pip install -r requirements.txt
# put the NHANES XPT files under data/raw/, see data/raw/README.md
python scripts/run_all.py

See HOW_TO_RUN.md for the full execution order, what each step depends on, and a complete map of which file holds which result.

Data source

NHANES is public. Download it from the CDC (https://wwwn.cdc.gov/nchs/nhanes/); data/raw/README.md lists the exact files for each cycle. Raw XPT files are not committed to this repository.

License

Code is MIT licensed (see LICENSE). NHANES data is a public-domain product of the U.S. National Center for Health Statistics and is not covered by that license - it is not redistributed here in any case, see above.

Languages

Python

93.1%

Jupyter Notebook

6.9%