egfr_calc
Estimated glomerular filtration rate by the race-free CKD-EPI 2021 creatinine equation, and optionally the 2021 creatinine-cystatin C equation, with creatinine in mg/dL or umol/L, reported in whole numbers as the NKF recommends and staged G1 to G5 (KDIGO 2012). Adults only. Every output carries a disclaimer: not medical advice, verify with a clinician.
Install
/tools install egfr_calc /tools install egfr_calc@1.0.0
In a console session, or let the model find it in normal mode: axlr_catalog describes it and axlr_install_tool asks for the same install card. The card reads catalog axlr · egfr_calc 1.0.0 · verified once the index signature, the manifest digest and every file digest check out. Then the model runs it with axlr_run_tool egfr_calc; each run is approved as a local_exec.
Arguments
One JSON object on stdin, validated against the schema before the program starts.
| name | type | required | default | meaning |
|---|---|---|---|---|
age | number | yes | Years. ≥ 18; ≤ 130 | |
sex | string | yes | one of "female", "male" | |
creatinine | number | string | yes | IDMS-standardised serum creatinine | |
creatinine_unit | string | no | "mg/dL" | 88.4 umol/L = 1 mg/dL. one of "mg/dL", "umol/L", "µmol/L" |
cystatin_c | number | string | no | Serum cystatin C in mg/L; adds eGFRcr-cys |
Input schema (JSON)
{
"type": "object",
"required": [
"age",
"sex",
"creatinine"
],
"additionalProperties": false,
"properties": {
"age": {
"type": "number",
"minimum": 18,
"maximum": 130,
"description": "Years"
},
"sex": {
"type": "string",
"enum": [
"female",
"male"
]
},
"creatinine": {
"type": [
"number",
"string"
],
"description": "IDMS-standardised serum creatinine"
},
"creatinine_unit": {
"type": "string",
"enum": [
"mg/dL",
"umol/L",
"µmol/L"
],
"default": "mg/dL",
"description": "88.4 umol/L = 1 mg/dL"
},
"cystatin_c": {
"type": [
"number",
"string"
],
"description": "Serum cystatin C in mg/L; adds eGFRcr-cys"
}
}
}
Example
Input
{
"age": 50,
"sex": "female",
"creatinine": 1.0
}
Output (stdout)
{
"egfr_cr": {
"egfr": 69,
"egfr_unrounded": "68.63",
"stage": "G2"
}
}
Files
The command the install card registers is python3 egfr_calc.py, run in the workspace with PATH and HOME as its only environment. Each file's SHA-256 is in the signed index; the console checks them before showing the card and again before writing.
egfr_calc.py · sha256 54de8eea8ba8122d1e808630874b6789790c1cdc1c9822ba4de79fd18fa01747
#!/usr/bin/env python3
"""Estimated GFR by the race-free CKD-EPI 2021 equations (Inker et al., N Engl J Med 2021)
and the KDIGO 2012 G stage.
Input (JSON on stdin):
age years, 18 or more (the equations are for adults)
sex "female" | "male"
creatinine serum creatinine (IDMS-standardised), number or decimal string
creatinine_unit "mg/dL" | "umol/L" (also "µmol/L") optional, default mg/dL; 88.4 umol/L = 1 mg/dL
cystatin_c serum cystatin C in mg/L optional: adds the creatinine-cystatin 2021 equation
eGFRcr = 142 x min(Scr/k, 1)^a x max(Scr/k, 1)^-1.200 x 0.9938^age x 1.012 [female]
eGFRcr-cys = 135 x min(Scr/k, 1)^b x max(Scr/k, 1)^-0.544 x min(Scys/0.8, 1)^-0.323
x max(Scys/0.8, 1)^-0.778 x 0.9961^age x 0.963 [female]
k = 0.7 female, 0.9 male; a = -0.241 female, -0.302 male; b = -0.219 female, -0.144 male.
"""
import json
import math
import sys
from decimal import ROUND_HALF_UP, Decimal
DISCLAIMER = ("Not medical advice: an arithmetic estimate from the published CKD-EPI 2021 equations. "
"Verify every result with a qualified clinician before any clinical use.")
UMOL_PER_MGDL = 88.4
STAGES = [(90, "G1", "Normal or high"), (60, "G2", "Mildly decreased"), (45, "G3a", "Mildly to moderately decreased"),
(30, "G3b", "Moderately to severely decreased"), (15, "G4", "Severely decreased"), (0, "G5", "Kidney failure")]
def fail(message):
print(json.dumps({"error": message, "disclaimer": DISCLAIMER}))
sys.exit(2)
def number(value, field, low, high):
if isinstance(value, bool) or not isinstance(value, (int, float, str)):
fail(f"{field} must be a number or a decimal string")
try:
result = float(Decimal(str(value).strip()))
except ArithmeticError:
fail(f"{field} must be a number or a decimal string")
if not math.isfinite(result) or not (low < result <= high):
fail(f"{field} must be greater than {low} and at most {high}")
return result
def stage(egfr):
for low, code, label in STAGES:
if egfr >= low:
return {"stage": code, "description": label}
def report(value):
whole = int(Decimal(repr(value)).quantize(Decimal(1), rounding=ROUND_HALF_UP))
out = {"egfr": whole, "egfr_unrounded": str(Decimal(repr(value)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)),
"reported": "<5" if whole < 5 else ">150" if whole > 150 else str(whole), "unit": "mL/min/1.73 m2"}
out.update(stage(whole))
return out
def main():
try:
request = json.load(sys.stdin)
except ValueError as error:
fail(f"stdin is not JSON: {error}")
if not isinstance(request, dict):
fail("input must be a JSON object")
extra = sorted(set(request) - {"age", "sex", "creatinine", "creatinine_unit", "cystatin_c"})
if extra:
fail(f"unknown field(s): {', '.join(extra)}")
if request.get("age") is None:
fail("age is required (do not calculate without it)")
age = number(request.get("age"), "age", 0, 130)
if age < 18:
fail("age under 18: the CKD-EPI 2021 equations are for adults; do not calculate")
sex = request.get("sex")
if sex not in ("female", "male"):
fail("sex must be female or male (the equations have a coefficient for each)")
unit = request.get("creatinine_unit", "mg/dL")
if unit not in ("mg/dL", "umol/L", "µmol/L", "μmol/L"):
fail("creatinine_unit must be mg/dL or umol/L")
if request.get("creatinine") is None:
fail("creatinine is required")
scr = number(request.get("creatinine"), "creatinine", 0, 40 if unit == "mg/dL" else 40 * UMOL_PER_MGDL)
if unit != "mg/dL":
scr /= UMOL_PER_MGDL
female = sex == "female"
kappa = 0.7 if female else 0.9
ratio = scr / kappa
egfr_cr = (142 * min(ratio, 1) ** (-0.241 if female else -0.302) * max(ratio, 1) ** -1.200
* 0.9938 ** age * (1.012 if female else 1))
result = {"creatinine_mg_dl": str(Decimal(repr(scr)).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)),
"egfr_cr": report(egfr_cr), "equation": "CKD-EPI 2021 creatinine (race-free)"}
if request.get("cystatin_c") is not None:
scys = number(request.get("cystatin_c"), "cystatin_c", 0, 20)
cys = scys / 0.8
egfr_cr_cys = (135 * min(ratio, 1) ** (-0.219 if female else -0.144) * max(ratio, 1) ** -0.544
* min(cys, 1) ** -0.323 * max(cys, 1) ** -0.778 * 0.9961 ** age * (0.963 if female else 1))
result["egfr_cr_cys"] = report(egfr_cr_cys)
result["equation"] += " and CKD-EPI 2021 creatinine-cystatin C"
result["notes"] = ["Reported in whole numbers, 5 to 150 (values outside print as <5 or >150), as the NKF "
"implementation guidance recommends; stage from the whole number (KDIGO 2012 G categories).",
"A CKD diagnosis needs persistence over 3 months and albuminuria; one eGFR is not a stage."]
result["source"] = "Inker et al., N Engl J Med 2021;385:1737-49; KDIGO 2012 CKD guideline"
result["disclaimer"] = DISCLAIMER
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
try:
main()
except SystemExit:
raise
except Exception as error: # last resort: a usage error, never a traceback
fail(f"invalid input ({type(error).__name__})")
Where it is served
The registry serves only AXLR clients, so a browser gets 403; fetch with a User-Agent that starts with axlr:
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/egfr_calc/1.0.0/manifest.json
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/egfr_calc/1.0.0/egfr_calc.py