Skip to main content
Classification Intermediate Clinical risk modelling

Can routine measurements screen for heart disease?

A classification study on 303 referred cardiac patients — and why the best-scoring model is not the one served.

Thirteen clinical measurements, 303 patients, and one question: how much of the diagnosis is visible before any invasive test is ordered? The study compares candidate models against a baseline, drops the two features that would have made it a better scorer and a worse screening tool, and reports what 76 test rows can and cannot establish.

Reading time:
11 min
Published:
15 Sep 2026
Updated:
15 Sep 2026
Dataset:
UCI Heart Disease — Cleveland
Model:
v1.0.0

The question

Coronary artery disease is confirmed by angiography — an invasive procedure. Before it is ordered, a clinician already has a patient’s age, blood pressure, cholesterol, resting ECG, and how they responded to an exercise test.

How much of the eventual diagnosis is already visible in those routine measurements?

This matters because the answer decides what a model like this could ever be for. If routine measurements carry most of the signal, a model built on them could help prioritise who gets referred. If they do not, then any model that scores well is quietly relying on tests that are only ordered once disease is already suspected — and it is answering a question that has already been answered.

The dataset here contains both kinds of feature, which makes the distinction testable rather than rhetorical.

What this study cannot answer

It cannot say whether any measurement causes heart disease. It cannot establish what would happen to a patient outside this sample. And it cannot produce a diagnosis — the label being predicted is the outcome of an angiogram that has already happened.

The data

303 patients referred for cardiac evaluation at the Cleveland Clinic Foundation in the late 1980s. Thirteen measurements each, plus an angiographic diagnosis.

The full feature dictionary, licence, checksum and collection limitations are on the dataset page.

The leakage audit

Every supervised study here runs a leakage audit before any model is fitted, and training aborts if a feature correlates 0.90 or above with the target. Nothing did — the strongest was thal at +0.538. No duplicate rows, no constant columns, no entity identifier that could span a split, no temporal ordering.

But the audit surfaced something the correlation check alone would have missed.

ca (vessels coloured by fluoroscopy) and thal (thallium stress test) are results of invasive follow-up tests. They precede the diagnosis label, so they are not leakage in the strict sense. They are also not available at the moment a screening decision is made — they are ordered because disease is already suspected. ca in particular is close to a direct measurement of the thing being predicted.

A model using them does not screen. It reports what the catheterisation found.

So the study trains two variants: one with everything, and a screening variant with ca and thal dropped. The screening variant is the one served below.

Rows
303
Columns
14

Collection limitations. Single institution, single era (Cleveland Clinic Foundation, late 1980s). A referral population with 45.9% disease prevalence, far above a general population — probabilities are conditioned on having been referred. Predominantly male. No socioeconomic, comorbidity or medication data.

What the data looks like

Class balance. 164 patients without angiographic disease, 139 with — a 45.9% positive rate. Close to balanced, which is unusual for a clinical dataset and is a direct consequence of this being a referred population rather than a screened one. It means accuracy is less actively misleading here than it would be on a rare outcome, though it is still not the metric that carries the decision.

Missingness. Six values across 303 rows, confined to ca (4) and thal (2) — the two invasive features, which is itself consistent with those tests not always having been performed.

Strongest associations with the diagnosis, on the training split:

Strongest feature correlations with the diagnosis, training split.
Feature Correlation with target
thal (thallium test) +0.538
ca (vessels by fluoroscopy) +0.471
thalach (max heart rate) −0.413
cp (chest pain type) +0.412
exang (exercise angina) +0.408

The two invasive tests top the list, which is exactly what makes them tempting and exactly why they were examined rather than simply used. Below them, the routine exercise-test measurements — maximum heart rate, chest pain type, exercise-induced angina — carry associations of similar magnitude.

Class balance

All 303 patients

Class balanceHorizontal bar chart. 164 patients had no angiographic disease and 139 had disease, out of 303 — a 45.9 percent positive rate.No disease164Disease139

What this shows. At 45.9% positive the classes are close to balanced, which is unusual for a clinical dataset and follows directly from this being a referred population rather than a screened one. It is also why accuracy is less actively misleading here than it would be on a rare outcome — though still not the metric that carries the decision.

Show the numbers
Class balance — underlying values
DiagnosisPatients
No disease164
Disease139

Source: Training run screening-v1

How the problem was framed

Supervised binary classification. The unit of observation is one patient at one evaluation.

Target. num records 0 for no disease and 1–4 for increasing angiographic severity. The study binarises to presence versus absence. This is the conventional framing for this dataset, and it discards real information: a borderline case and a severe case become the same label. That is a choice the study made, not a property of the data.

Validation. A stratified 75/25 split with a fixed seed, giving 227 training and 76 test rows. Stratified because 303 rows are few enough that an unstratified split can shift test-set prevalence enough to move recall by several points on its own.

Model selection used 5-fold cross-validated recall on the training split only. The test split was evaluated once, at the end. Repeatedly consulting a 76-row test set while choosing between models would turn it into a second training set.

The metric that decides. Recall on the positive class. In a screening frame, a missed case costs more than an additional referral — so a model that trades precision for recall is making the right trade, within reason. Accuracy is reported, never headlined.

Preprocessing

All preprocessing lives inside the serialised pipeline, so the model served below cannot apply a different transformation from the one it was trained with. This is not a stylistic preference: a preprocessing step applied separately at inference time is one of the few bugs that produces plausible wrong answers rather than an error.

Preprocessing steps and the choice made for each.
Step Applied to Choice
Imputation numeric Median
Imputation categorical Most frequent
Scaling numeric Standardisation
Encoding categorical One-hot, unknown categories ignored

Imputation statistics are fitted on the training split only. Fitting them on the full dataset before splitting is the most common quiet form of leakage — the test rows contribute to a median that is then used to fill training rows.

Unknown categories are ignored rather than raising, because a visitor to the lab below can submit a combination that 227 training rows never contained, and a server error is the wrong answer to that. The cost is recorded in the limitations: such an input is extrapolated silently.

Candidate models

Majority-class baseline. Predicts ‘no disease’ for everyone. It exists because without it, no other number means anything. It achieves 53.9% accuracy and 0.000 recall — a clean illustration of why accuracy was not chosen as the headline metric.

Logistic regression (C=1.0, class_weight='balanced'). Considered because its coefficients are directly inspectable, it is well suited to 227 rows, and it produces probability estimates natively. Assumes a linear relationship between features and log-odds — a real constraint, taken deliberately.

Random forest (300 trees, min_samples_leaf=3, balanced). Considered because it captures interactions and non-linearities logistic regression cannot. Risks overfitting a small sample and is harder to explain.

Both use balanced class weights so the slightly smaller positive class is not under-weighted. This has a consequence that matters later: balanced weighting deliberately distorts predicted probabilities away from the true base rate, which is why an explicit calibration step follows.

Evaluation

On the held-out test split of 76 patients. Intervals are 95% percentile bootstrap, 2000 resamples.

The comparison

See the table below. Then read this, because the table is easy to misread:

None of these models is distinguishable from the others. Logistic regression scores 0.886 recall and the random forest 0.857, but the intervals are [0.7778, 0.9737] and [0.7333, 0.9677] — they overlap almost entirely. With 76 test rows, a single patient moving from one side of the threshold to the other shifts recall by more than a point.

What the numbers do establish is that every candidate is far better than the baseline, which is a much weaker claim than a leaderboard implies, and the honest one.

Why logistic regression was selected

Not because it scored highest. It was selected because when scores are indistinguishable, the tiebreakers are interpretability, robustness on a small sample, and cost to serve — and it wins all three. Choosing the top row of a table whose rows are statistically identical is how a model gets picked for noise.

Confusion matrix

Of 35 patients who had disease, the model identified 30 and missed 5. Of 41 who did not, it correctly cleared 36 and flagged 5 who were disease-free.

In a screening frame those five false positives are the acceptable cost of the trade. The five false negatives are the number that matters, and the limitations section says who they tend to be.

Did dropping the invasive tests hurt?

Measurably, no. The full-feature variant reached 0.857 recall; the screening variant reached the same, with overlapping intervals. On this sample the routine measurements carry the available signal. That result is what justifies serving the screening model — it gives up nothing detectable and answers a question worth asking.

Recall

0.857

95% CI [0.7333, 0.9677] · the headline metric

Precision

0.857

Positive class, test split

ROC AUC

0.925

Ranking quality

Brier score

0.117

Calibration — lower is better

Candidate model comparison for Can routine measurements screen for heart disease?
Model Recall 95% CI Precision Accuracy ROC AUC
Majority-class baseline Baseline 0.000 — 0.000 0.539 0.500
Logistic regression 0.886 [0.7778, 0.9737] 0.861 0.882 0.923
Random forest 0.857 [0.7333, 0.9677] 0.811 0.842 0.927
Logistic regression, calibrated Selected 0.857 [0.7333, 0.9677] 0.857 0.868 0.925

Every candidate's interval overlaps every other candidate's. The selected row is not the highest scorer; it is the most defensible one when the scores cannot be told apart.

ROC curves

Held-out test split, 76 patients

ROC curvesROC curves for two models against the chance diagonal. Logistic regression reaches an AUC of 0.9233; Random forest reaches an AUC of 0.9275. Both curves sit well above chance and close to each other.000.250.250.50.50.750.7511ChanceLogistic regressionAUC 0.9233Random forestAUC 0.9275False positive rateTrue positive rate

What this shows. Both models rank cases far better than chance and almost identically to each other. The gap between the two curves is smaller than the uncertainty in either of them at 76 test rows — which is the same reason the model comparison table cannot be read as a ranking.

Show the numbers
ROC curves — underlying values
ModelROC AUC
Logistic regression0.9233
Random forest0.9275

Source: Training run screening-v1

Confusion matrix

Selected model on the test split

Confusion matrixTwo by two matrix. Of patients with disease, 30 were flagged and 5 were missed. Of patients without disease, 36 were cleared and 5 were flagged.Model saysClearedFlaggedNo diseaseDisease36correctly cleared5false alarms5missed cases30correctly flaggedActually

What this shows. The 5 false positives are the acceptable cost of a screening trade — each is an extra referral. The 5 false negatives are the number that matters, and the limitations section identifies who they tend to be.

Show the numbers
Confusion matrix — underlying values
ActualModel saysPatients
No diseaseCleared36
No diseaseFlagged5
DiseaseCleared5
DiseaseFlagged30

Source: Training run screening-v1

What the model leans on

Logistic regression coefficients are directly inspectable, which is most of why it was chosen. The served model reports the inputs it weighs most heavily alongside each prediction.

One caveat is stated wherever those weights appear: they are global importances for the fitted model, not an attribution for the individual prediction on screen. A feature with a large coefficient influenced the model’s overall behaviour; it did not necessarily drive this particular result. Per-prediction attribution would need SHAP or a comparable method, which is not justified for a linear model on eleven features.

On calibration

The model is explicitly calibrated using Platt scaling, and the reason is specific: class_weight='balanced' deliberately shifts the decision boundary toward the positive class, which pushes raw predicted probabilities away from the population base rate. Without correction the numbers would rank cases correctly while being wrong as probabilities.

Reported honestly: the calibrated Brier score (0.117) is not better than the uncalibrated logistic regression’s (0.111). At 76 test rows that difference is noise. The calibration step is kept because it is the theoretically correct response to the class weighting, not because the evidence shows it helped.

And the probability remains calibrated to a referred population with 45.9% prevalence — not to a general one.

What the model weighs

Absolute coefficient magnitude, one-hot terms folded back onto their source column

What the model weighsHorizontal bar chart of coefficient magnitude by input. Chest pain type 2.3218, Sex 1.3491, ST segment slope 0.8645, Resting ECG 0.5083, Max heart rate 0.4952.Chest pain type2.32Sex1.35ST segment slope0.86Resting ECG0.51Max heart rate0.50ST depression0.46Exercise angina0.43Fasting blood sugar0.38

What this shows. Chest pain type dominates, which matches clinical intuition for this population. These are global weights for the fitted model — they describe how it behaves overall, not why it produced any particular prediction.

Show the numbers
What the model weighs — underlying values
InputCoefficient magnitude
Chest pain type2.32
Sex1.35
ST segment slope0.86
Resting ECG0.51
Max heart rate0.50
ST depression0.46
Exercise angina0.43
Fasting blood sugar0.38

Source: Training run screening-v1

Try it

The screening model, running live. Every input is a measurement available before any invasive test — that is the whole point of this variant.

The output is a class label and a calibrated probability, the decision threshold that produced it, and the model’s semantic version. Read the probability as “among referred cardiac patients in this 1988 sample with similar measurements, roughly this proportion had angiographic disease.” It is not a statement about you, and the ranges below are the ranges the model actually saw.

Age at examination. Valid range 34.0–77.0.

Recorded sex: 0 female, 1 male.

1 typical angina, 2 atypical angina, 3 non-anginal pain, 4 asymptomatic.

Resting blood pressure on admission. Valid range 94.0–192.0.

Serum cholesterol. Valid range 126.0–564.0.

1 if fasting blood sugar exceeds 120 mg/dl.

0 normal, 1 ST-T abnormality, 2 probable left ventricular hypertrophy.

Maximum heart rate achieved during exercise testing. Valid range 71.0–195.0.

1 if exercise induced angina.

ST depression induced by exercise relative to rest. Valid range 0.0–7.0.

Slope of the peak exercise ST segment: 1 upsloping, 2 flat, 3 downsloping.

Model v1.0.0 · no account required

What this does and does not show

What was observed. Among 303 referred cardiac patients, routine pre-angiographic measurements — chest pain type, exercise-induced angina, maximum heart rate, ST depression — are associated with angiographic disease strongly enough that a linear model separates the classes well on held-out data.

What the model does. It identifies 86% of positive cases in the test split, with a 95% interval of [0.7333, 0.9677], at 86% precision.

What is inferred. That the invasive tests were not carrying the signal alone. Removing both cost nothing measurable, which suggests the routine measurements already contain most of what this dataset has to offer.

What cannot be concluded.

  • That any measurement causes heart disease. This is an observational association, not a causal design.
  • That logistic regression is better than a random forest for this problem. The confidence intervals say those models are indistinguishable here.
  • That these numbers would hold in any other population, era, or care setting.
  • That a probability from this model applies to an individual outside the referred population it was fitted on.

The most useful result is not the recall figure. It is that examining when a feature becomes available changed which model got deployed — and cost nothing to do.

Limitations

Ordered by how much each should change your reading of the result.

The two that matter most: the test set has 76 rows, so every interval here is wide enough that model comparisons do not survive it; and this is a referral population, so the probabilities are conditioned on someone having already decided this patient warranted cardiac evaluation.

  • 76 test rows — the intervals are wider than the numbers look

    Recall's 95% interval spans nearly a quarter. Every model compared falls inside every other model's interval, so no claim that one is better than another is supported. Model selection rested on interpretability for exactly this reason.

  • A referral population, not a screening population

    Prevalence in this sample is 45.9%; in an unselected population it is a small fraction of that. A calibrated probability here is calibrated to referred patients and would over-predict substantially elsewhere — and no amount of internal validation would reveal that.

  • Recall is materially worse in younger patients

    0.714 for patients aged 56 or under (n=40) against 0.952 for those over 56 (n=36). Age is itself a risk factor and the model appears to lean on it, making it least reliable precisely where a screening tool would need to be most reliable. Subgroup counts are small, so this is indicative rather than precise.

  • One institution, one era

    Cleveland Clinic Foundation, late 1980s. Diagnostic criteria, measurement practice and treatment have all moved since. External validity is unestablished, not merely unmeasured.

  • The target collapses four severity levels into one bit

    A borderline case and a severe case become the same label. This is the conventional framing and it makes the problem tractable at n=303, but it discards information the data contains.

  • Calibration is theoretically motivated, not demonstrated

    Platt scaling is applied because balanced class weighting de-calibrates raw output. But the calibrated Brier score (0.117) is not better than the uncalibrated one (0.111); at this sample size the step cannot be shown to have helped.

  • Unknown inputs extrapolate silently

    The encoder ignores category values absent from training rather than failing. A lab visitor can submit a combination the model never saw and receive a confident-looking number with no indication of it.

  • This is not a medical device

    An educational demonstration. Not diagnostic, no regulatory clearance, never prospectively validated, and not a basis for any decision about a real person.

Recall by subgroup

Test split only — small counts, indicative not precise

Recall by subgroupHorizontal bar chart of recall by subgroup. Female (n=26) 1.000, Male (n=50) 0.808, Age 56 and under (n=40) 0.714, Over 56 (n=36) 0.952.Female (n=26)1.000Male (n=50)0.808Age 56 and under (n=40)0.714Over 56 (n=36)0.952

What this shows. Recall falls to 0.714 for patients aged 56 and under against 0.952 for those over 56. Age is itself a risk factor and the model appears to lean on it, so it is least reliable exactly where a screening tool would need to be most reliable. These subgroup counts are small — read the direction, not the decimals.

Show the numbers
Recall by subgroup — underlying values
SubgroupRecall
Female (n=26)1.000
Male (n=50)0.808
Age 56 and under (n=40)0.714
Over 56 (n=36)0.952

Source: Training run screening-v1

References

Sources are listed below.

Concepts in this study

Recall — of everyone who actually had the condition, the share the model found. It is the metric that carries the decision here because a missed case costs more than an extra referral.

Precision — of everyone the model flagged, the share who actually had it. Recall and precision trade against each other; moving the threshold buys one with the other.

Calibration — whether a predicted probability of 0.7 corresponds to the event happening about 70% of the time. A model can rank cases perfectly and still be badly calibrated, which is why ranking metrics like AUC and calibration metrics like the Brier score are both reported.

Class weighting — telling the model to treat errors on the smaller class as more costly. It improves recall and, as a side effect, de-calibrates the raw probabilities, which is why a calibration step follows it here.

Target leakage — a feature that encodes the answer. The strict form is caught by a correlation check. The form that caught this study is subtler: a feature legitimately measured before the label, but not available at the moment the prediction would need to be made.

Bootstrap confidence interval — resampling the test set with replacement to see how much a metric moves. On 76 rows it moves a great deal, which is the single most important thing to know about every number on this page.

Glossary

Recall Theory in Axiomizer →
Of everyone who actually had the condition, the share the model found. It is this study's headline metric because a missed case costs more than an extra referral.
Precision Theory in Axiomizer →
Of everyone the model flagged, the share who actually had the condition. Precision and recall trade against each other as the decision threshold moves.
Calibration Theory in Axiomizer →
Whether a predicted probability of 0.7 corresponds to the event happening about 70% of the time. A model can rank cases perfectly and still be badly calibrated.
Brier score Theory in Axiomizer →
The mean squared error of predicted probabilities. Lower is better. It measures calibration, which ranking metrics like AUC do not.
Class weighting Theory in Axiomizer →
Telling the model that errors on the smaller class cost more. It raises recall and, as a side effect, de-calibrates the raw probabilities.
Target leakage Theory in Axiomizer →
A feature that encodes the answer. The strict form shows up as a very high correlation with the target; the subtler form is a feature that is legitimate but unavailable at the moment the prediction would need to be made.
Bootstrap confidence interval Theory in Axiomizer →
Resampling the test set with replacement to see how much a metric moves. On 76 rows it moves a great deal, which is the most important thing to know about these numbers.
Baseline Theory in Axiomizer →
The simplest possible predictor — here, always guessing the majority class. Without it, no other score means anything.

Check your understanding

6 questions on the reasoning in this study. Every answer comes with an explanation, and no account is needed.

Take the quiz

References

  1. Janosi, A., Steinbrunn, W., Pfisterer, M., & Detrano, R. (1989). Heart Disease [Dataset]. UCI Machine Learning Repository. doi:10.24432/C52P4X Link (accessed 14 Sep 2026)
  2. Detrano, R., Janosi, A., Steinbrunn, W., et al. (1989). International application of a new probability algorithm for the diagnosis of coronary artery disease. American Journal of Cardiology, 64(5), 304-310. doi:10.1016/0002-9149(89)90524-9 (accessed 14 Sep 2026)
  3. Platt, J. (1999). Probabilistic outputs for support vector machines and comparisons to regularized likelihood methods. Advances in Large Margin Classifiers. (accessed 14 Sep 2026)
  4. Pedregosa, F., et al. (2011). Scikit-learn: Machine Learning in Python. Journal of Machine Learning Research, 12, 2825-2830. Link (accessed 14 Sep 2026)

Reproducibility

Model version
1.0.0
Artifact checksum
92edd6e3e114ad0c…
seed
42
python
3.13.5
git_sha
8e67a8a
trained_at
2026-09-15T01:06:22.900488+00:00
scikit_learn
1.6.0
dataset_checksum
a74b7efa387bc9d108d7d0115d831fe9b414b29ae7124f331b622b4efa0427c8

Discussion

Sign in to join the discussion. Reading needs no account.

No comments yet.