NUMERICAL AUDIT · 1 NOVEMBER 2026

statsmodels: survival quantiles depended on rounding at exact ties; fix merged

SurvfuncRight.quantile compared an exact Kaplan–Meier value of 1/2, stored one rounding step below 1/2, with a strict inequality and could return the previous event time; the tolerance fix was merged on 30 October 2026.

Xamit Kadirbekov, Daniyal Kadirbekov · AI-assisted investigation and writing

Checked 1 November 2026: PR #10648 merged on 30 October 2026 (UTC); no release containing the change has been independently tested.

Upstream report #10648

Authors: Xamit Kadirbekov and Daniyal Kadirbekov (GERO). Published 1 November 2026; finding dated 30 October 2026.

Status: Checked 1 November 2026: PR #10648 merged on 30 October 2026 (UTC); no release containing the change has been independently tested.

SurvfuncRight.quantile compared an exact Kaplan–Meier value of 1/2, stored one rounding step below 1/2, with a strict inequality and could return the previous event time; the tolerance fix was merged on 30 October 2026.

Published proposal

What the function does

SurvfuncRight.quantile (statsmodels/duration/survfunc.py) returns survival quantiles from a Kaplan–Meier curve, most often the median survival time. Its documented rule, following SAS, is a strict inequality: the first time with S(t) < 1 − p. We checked main e82bcf02f2cb7fd915264f6af79a51ca394965e8 and release 0.15.0, which had the same code.

The discrepancy

surv_prob is computed as exp(cumsum(log(...))), so an exact 1/2 can be stored as 0.49999999999999994. The strict comparison then picks the event time where the curve reaches 1/2 instead of the next one.

import numpy as np
from statsmodels.duration.survfunc import SurvfuncRight

sf = SurvfuncRight(np.arange(1, 13, dtype=float), np.ones(12))
sf.surv_prob[5]      # 0.49999999999999994  (exactly 1/2)
sf.quantile(0.5)     # 6.0 — by the documented strict rule the answer is 7

sf2 = SurvfuncRight(np.repeat(np.arange(1, 13, dtype=float), 2), np.ones(24))
sf2.quantile(0.5)    # 7.0 — same curve, different answer

With 10 patients the same code gives the rule's answer; with 12 and 18 it does not. On a censored 18-patient example in months, where KM is exactly 1/2 at month 12 and the next event is at month 16, statsmodels 0.15.0 reported 12, its own rule gives 16, and R and SAS (midpoint) give 14. On the same data lifelines reported 16, with the opposite rounding error (separate note). In 1,500 simulated small datasets (10–60 subjects), statsmodels was wrong in 123 of 4,500 quartiles and the median differed from the exact-arithmetic answer in 87 datasets (about 6%).

Reference and why this is not a convention

The reference is the documented strict rule evaluated on KM products in exact fractions.Fraction arithmetic. For the 12-patient curve R and SAS report the midpoint 6.5; 6 matches no convention. The choice of rule is a convention; an answer that changes with the 16th decimal digit, or with duplicating every patient, is not.

Fix and tests

Upstream status

PR #10648 was merged on 30 October 2026 at 21:18 UTC. No formal review comments were posted; the merge by a maintainer is the upstream acceptance. On 1 November we confirmed the merged state with the GitHub CLI; we have not installed or tested a statsmodels release containing the change, and we do not know which release will ship it.

Limits

The log-rank test is not affected. The confidence interval of the quantile (quantile_ci) was not checked separately. The scale is shown on simulated small samples; exact flat steps at 1/2 are rarer in large samples. No patient data were used, and we make no claim about errors in published studies. Novelty rests on a bounded GitHub search of issues and PRs; it is not an exhaustive priority claim.

The example was re-run in a separate session on 30 October before submission. No reproduction archive is attached to this page yet.

Attribution and license

AI tools (Claude) helped find the cases, build the reference calculations and draft the patches and tests; the examples were re-run independently before submission. Xamit Kadirbekov and Daniyal Kadirbekov are the report authors and reviewed the submitted diff; upstream software remains credited to its developers. Thank you to the statsmodels maintainers for the quick review and merge. Report: CC BY 4.0. Upstream patches retain their upstream licenses.