Medical AI · Applied case studies: diabetes & metabolic health · Lesson 3 of 5
Diabetic kidney disease risk modelling
Reviewed by Dr. Ram Kumar G, MBBS, MD (Internal Medicine), MPH (USA), on · Last updated
- Intermediate
- Clinical focus
- About 30 minutes
- Includes exercise
Before this lesson: Survival analysis: time-to-event models, A logistic-regression risk score
In short
In diabetes, the risk of kidney failure depends above all on two tests: eGFR, which measures kidney function, and urine albumin-to-creatinine ratio (ACR), which measures kidney damage. KDIGO combines them into a colour-coded 'heat map' of risk categories, and validated equations such as the Kidney Failure Risk Equation turn them into an individual 2- or 5-year risk. On synthetic data, a continuous model using the same tests ranked patients slightly better than the categories, and sparse categories gave unreliable estimates.
You will learn to
- Explain the KDIGO eGFR (G) and albuminuria (A) categories and the risk heat map
- Rebuild a heat map of observed risk from synthetic data
- Compare category-based and continuous risk estimates
- Describe the Kidney Failure Risk Equation and when guidelines recommend it
The two tests that matter most
Guidelines recommend checking eGFR and urine ACR at least once a year in people with diabetes. KDIGO classifies chronic kidney disease by eGFR category (G1 to G5) and albuminuria category (A1 to A3), and shows the combined risk as a heat map from low to very high.
| Category | Definition |
|---|---|
| G1 / G2 | eGFR 90 or more / 60–89 mL/min/1.73m² |
| G3a / G3b | eGFR 45–59 / 30–44 |
| G4 / G5 | eGFR 15–29 / below 15 (kidney failure) |
| A1 / A2 / A3 | ACR below 30 / 30–300 / above 300 mg/g |
Rebuilding the heat map
import numpy as np
import pandas as pd
rng = np.random.default_rng(12)
n = 20000
df = pd.DataFrame({
"age": rng.normal(62, 11, n).clip(30, 90).round(),
"egfr": rng.gamma(9, 7.5, n).clip(8, 130).round(), # mL/min/1.73m2
"acr": np.exp(rng.normal(3.2, 1.6, n)).clip(2, 3000).round(), # urine albumin-to-creatinine, mg/g
})
# Synthetic 5-year risk of kidney failure: higher with lower eGFR and higher albuminuria
logit = -2.6 - 0.075 * (df.egfr - 45) + 0.55 * (np.log(df.acr) - np.log(30)) - 0.01 * (df.age - 62)
df["kidney_failure_5y"] = rng.binomial(1, 1 / (1 + np.exp(-logit)))
df["G"] = pd.cut(df.egfr, [0, 15, 30, 45, 60, 90, 999], right=False,
labels=["G5", "G4", "G3b", "G3a", "G2", "G1"])
df["A"] = pd.cut(df.acr, [0, 30, 300, 1e9], right=False, labels=["A1", "A2", "A3"])
heat = df.pivot_table(index="G", columns="A", values="kidney_failure_5y", aggfunc="mean", observed=False)
print((heat.loc[["G1", "G2", "G3a", "G3b", "G4", "G5"]] * 100).round(1).to_string())
# A A1 A2 A3
# G
# G1 0.0 0.2 0.5
# G2 0.3 2.1 4.4
# G3a 1.7 6.7 18.2
# G3b 7.1 16.5 42.3
# G4 19.7 39.4 52.2
# G5 0.0 50.0 NaN- Risk rises down the rows (lower eGFR) and across the columns (more albuminuria), the pattern the KDIGO heat map summarises.
- The G5 row looks odd: 0% for A1 and no value for A3. The next block shows why.
Sparse categories
counts = df.pivot_table(index="G", columns="A", values="kidney_failure_5y", aggfunc="size", observed=False)
print(counts.loc[["G1", "G2", "G3a", "G3b", "G4", "G5"]].to_string())
# A A1 A2 A3
# G
# G1 1710 1247 192
# G2 4916 3457 477
# G3a 2708 1992 330
# G3b 1456 1014 137
# G4 193 142 23
# G5 4 2 0- Only six synthetic people are in G5, so its 'risks' are meaningless. Real datasets have the same problem in their most severe and rarest cells, where decisions matter most.
Categories versus a continuous model
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
train, test = train_test_split(df, test_size=0.3, random_state=0, stratify=df.kidney_failure_5y)
# 1. Heat-map cell: use the observed risk of each G x A cell in the training data
cell_risk = train.groupby(["G", "A"], observed=False).kidney_failure_5y.mean()
p_cell = test.set_index(["G", "A"]).index.map(cell_risk).to_numpy(dtype=float)
# 2. Continuous model using the same information
features = lambda d: np.column_stack([d.egfr, np.log(d.acr), d.age])
model = LogisticRegression(max_iter=1000).fit(features(train), train.kidney_failure_5y)
p_model = model.predict_proba(features(test))[:, 1]
print(f"KDIGO heat-map categories: AUROC {roc_auc_score(test.kidney_failure_5y, p_cell):.3f}")
print(f"Continuous eGFR, log ACR, age: AUROC {roc_auc_score(test.kidney_failure_5y, p_model):.3f}")
# KDIGO heat-map categories: AUROC 0.849
# Continuous eGFR, log ACR, age: AUROC 0.867- The continuous model ranks patients slightly better (AUROC 0.867 against 0.849) because it uses exact values rather than bands, and it gives a risk even in sparse cells.
- Categories remain useful for communication and for guideline thresholds; individual risk equations add precision for decisions such as referral timing.
The Kidney Failure Risk Equation
The Kidney Failure Risk Equation (KFRE) estimates the 2- and 5-year risk of kidney failure from age, sex, eGFR and ACR. It was developed in Canada (Tangri et al., 2011) and validated in more than 700,000 people from over 30 countries (Tangri et al., 2016). The KDIGO 2024 guideline recommends using a validated risk equation such as the KFRE for people with eGFR categories G3 to G5 to guide decisions such as referral and planning.
- Use the published, validated equation for clinical estimates; the synthetic model in this lesson is only for learning.
- Kidney failure is not the only outcome: people with diabetic kidney disease are also at high risk of cardiovascular events and death, which affect what the kidney risk means for each person.
Teaching models, not clinical tools
- Data in these case studies are simulated, and the models are fitted to them for learning. They must not be used to make decisions about real patients.
- Validated tools exist for several of these problems; any tool used in care must be validated for its population and meet regulatory requirements.
Exercise
Model kidney risk in diabetes with categories, continuous predictors and survival methods.
Data: Synthetic cohort generated in the notebook (no real patient data) (Generated in the notebook)
- Reproduce the heat map and cell counts, and add bootstrap confidence intervals to each cell's risk.
- Add follow-up time and censoring, and refit the model as a Cox model from the survival analysis lesson.
- Add a competing risk of death and compare kidney failure risk estimates with and without it.
- Check calibration of the continuous model by decile of predicted risk.
- Write a short explanation of a patient's 5-year risk that a person with diabetes could understand.
Open the notebook in ColabView on GitHubWorked solution coming soon
Key takeaways
- eGFR and albuminuria together drive kidney risk in diabetes; check both at least yearly.
- The KDIGO heat map summarises risk by G and A category.
- Continuous models rank risk slightly better and handle sparse categories.
- Use validated equations such as the KFRE for clinical estimates.
Terms used: AUROC (area under the ROC curve), Calibration, Competing risks
Related: Diabetic kidney disease, Survival analysis, External validation and dataset shift
FAQ
What is the KDIGO heat map?
A grid that combines eGFR categories (G1 to G5) and albuminuria categories (A1 to A3) into colour-coded levels of risk for chronic kidney disease outcomes.
What is the Kidney Failure Risk Equation?
A validated equation that estimates the 2- and 5-year risk of kidney failure from age, sex, eGFR and urine albumin-to-creatinine ratio, recommended by KDIGO for people with eGFR categories G3 to G5.
Why is albuminuria so important?
Albumin in the urine is a sign of kidney damage and strongly predicts both faster loss of kidney function and cardiovascular events, independently of eGFR.
Can machine learning beat the KFRE?
Some studies report small gains from more predictors or flexible models, but the KFRE is simple, transparent and widely validated. Any new model would need equally broad validation.
How often should kidney tests be done in diabetes?
At least once a year for eGFR and urine ACR, and more often in people with kidney disease, as guided by their care team.
Sources
- Kidney Disease: Improving Global Outcomes (KDIGO) CKD Work Group. KDIGO 2024 Clinical Practice Guideline for the Evaluation and Management of Chronic Kidney Disease. Kidney Int 2024;105(4S):S117–S314. doi:10.1016/j.kint.2023.10.018
- KDIGO Diabetes Work Group. KDIGO 2022 Clinical Practice Guideline for Diabetes Management in Chronic Kidney Disease. Kidney Int 2022;102(5S):S1–S127. doi:10.1016/j.kint.2022.06.008
- Tangri N, et al. A predictive model for progression of chronic kidney disease to kidney failure. JAMA 2011;305(15):1553–1559. doi:10.1001/jama.2011.451
- Tangri N, et al. Multinational assessment of accuracy of equations for predicting risk of kidney failure: a meta-analysis. JAMA 2016;315(2):164–174. doi:10.1001/jama.2015.18202
- American Diabetes Association, Standards of Care in Diabetes—2026. Diabetes Care 2026;49(Suppl 1)
Educational content only. This material teaches methods; it is not clinical advice. Code, models and examples here are not validated medical devices and must not be used to make decisions about real patients without proper clinical validation, governance and any required regulatory approval. Examples use synthetic or openly licensed de-identified data only. Never paste identifiable patient information into public AI tools. Mentions of models, tools or vendors are for identification, not endorsement.