Does prespecified covariate adjustment in the Cox model really increase power over the unadjusted log-rank test in a randomised survival trial? A real-data resampling study of the NCCTG adjuvant colon cancer trial
Paper
Covariate adjustment does not automatically buy power in a randomised survival trial: a plasmode resampling study of the NCCTG adjuvant colon cancer trial
Abstract
Prespecified adjustment for prognostic baseline covariates is routinely recommended to improve the efficiency of randomised trials. For the Cox model the argument is indirect: adjustment inflates the standard error of the treatment coefficient, but non-collapsibility inflates the conditional estimand by more, so the test is expected to gain power. That reasoning has been validated almost entirely by parametric simulation, in which covariates are balanced in expectation by construction. We tested it in a real trial's own resampling world. Using the 594 complete-case death records of the observation and levamisole-plus-fluorouracil arms of the NCCTG/Mayo adjuvant colon cancer trial, we built three plasmode data-generating mechanisms by bootstrapping the observed records: as-randomised, risk-balanced, and a permutation null. Over 4,000 replicates per cell at 150 and 300 patients per arm we compared the log-rank test, unadjusted Cox, Cox adjusted for eight prespecified covariates, Cox with within-replicate backward elimination, and restricted mean survival time (RMST) differences at 5 and 6.5 years. Adjustment lost power under the as-randomised mechanism, by 5.08 percentage points (Monte Carlo standard error, MCSE, 0.62) at 150 per arm and 2.70 points (0.43) at 300, because the trial's realised covariate imbalance cancelled the non-collapsibility inflation (estimand ratio 0.998) while the standard-error cost (ratio 1.035) remained. Resampling with baseline risk balanced restored the textbook gain (+9.55 points, MCSE 0.56). The adjusted Wald test was also anti-conservative (6.35% and 6.45% at alpha = 0.05). Trialists should treat the efficiency gain from Cox adjustment as a population property, not a guarantee for their trial.
§Ask about this paper
Grounded in this runQuestions are answered from this run's record — the paper, the executed analysis, the data provenance and the referee report. Nothing else is consulted.
This run is finished, so the recorded paper cannot be rewritten. Questions are still answered from its record.
Pipeline
1.Literature review
Literature review complete (live:agent:claude-opus-5): 10 key papers, 18 grounded sources, 6 open gaps.
Summary
Adjustment for prognostic baseline covariates is the standard recommendation for improving the efficiency of randomised trials, and the FDA's 2023 final guidance endorses it explicitly. For linear models the argument is uncontroversial: the estimand is unchanged and the standard error falls. For non-linear models it is not. Robinson and Jewell (1991) showed for logistic regression that adjustment inflates the standard error of the log odds ratio, but moves the estimand further from the null by more, so the test gains power. Ford, Norrie and Ahmadi (1995) established the survival analogue: if the conditional hazards are proportional the marginal hazards generally are not, so the adjusted and unadjusted Cox models are mutually inconsistent and the crude log hazard ratio is attenuated toward zero. Lin and Wei (1989) makes the crude fit interpretable anyway, as a consistent estimator of a well-defined limiting parameter under misspecification.
The empirical case that adjustment pays is built almost entirely on parametric simulation. Hernandez, Steyerberg and Habbema (2004) demonstrated it for binary outcomes; Hernandez, Eijkemans and Steyerberg (2006) is the closest antecedent for time-to-event outcomes, quantifying the power gain as a function of covariate strength; Momal et al. (2023) report sample-size reductions from 3.1% to 85.7% depending on prognostic C-index and event rate. Kahan et al. (2014) took the complementary empirical route across 12 outcomes from 8 real trials and found the gain to be real but conditional on the covariates genuinely being prognostic.
Two things are missing. First, in a parametric simulation covariates are balanced in expectation by construction; in a real trial the realised imbalance is a fixed feature that moves the adjusted estimate for reasons unrelated to non-collapsibility, and can offset the estimand inflation entirely. Second, the comparison is rarely decomposed into its two ingredients, the estimand ratio and the standard-error ratio, whose relative size determines the sign of the power difference. Plasmode designs that resample real records address the first gap; Shaw et al. (2026) caution that the reference value must be defined inside the resampling population. Morris, White and Crowther (2019) supply the ADEMP reporting frame and, crucially, the point that methods applied to the same replicate are correlated, so head-to-head differences need paired Monte Carlo standard errors. Tian et al. (2018) and Eaton et al. (2020) supply the restricted mean survival time as an alternative estimand whose real-data efficiency relative to the log-rank test is also usually assessed only under parametric hazards.
Key papers (10)
- Model inconsistency, illustrated by the Cox proportional hazards model. Ford, I., Norrie, J. and Ahmadi, S.; Statistics in Medicine 14(8), 735-746, 1995.The canonical statement of non-collapsibility for the Cox model: adjusted and unadjusted Cox models cannot both be correct, and the marginal log hazard ratio is attenuated toward zero relative to the conditional one. Supplies the theoretical prediction the study tests.
- Some surprising results about covariate adjustment in logistic regression models. Robinson, L.D. and Jewell, N.P.; International Statistical Review 59(2), 227-240, 1991.Establishes the two-ingredient mechanism - standard error inflation offset by a larger estimand shift - that underlies every claim that adjustment gains power in a non-linear model. The decomposition used in this study is its direct analogue for the Cox model.
- Randomized controlled trials with time-to-event outcomes: how much does prespecified covariate adjustment increase power?. Hernandez, A.V., Eijkemans, M.J.C. and Steyerberg, E.W.; Annals of Epidemiology 16(1), 41-48, 2006.The closest antecedent: a parametric simulation study asking exactly the present question for survival endpoints, and reporting power gains and sample-size savings from prespecified adjustment. The present study asks the same question with a real trial as the data-generating mechanism.
- The risks and rewards of covariate adjustment in randomized trials: an assessment of 12 outcomes from 8 studies. Kahan, B.C., Jairath, V., Dore, C.J. and Morris, T.P.; Trials 15, 139, 2014.The main real-trial evidence base for the size of the adjustment gain, and the paper that most clearly separates the reward from the risk of adjusting for weakly prognostic covariates.
- More efficient and inclusive time-to-event trials with covariate adjustment: a simulation study. Momal, R., Li, H., Trichelair, P., Blum, M.G.B. and Balazard, F.; Trials 24, 380, 2023.The most recent time-to-event adjustment simulation, reporting sample-size reductions of 3.1% to 85.7% as a function of prognostic C-index and cumulative incidence; provides the quantitative benchmark against which a real-data result can be judged.
- Using simulation studies to evaluate statistical methods. Morris, T.P., White, I.R. and Crowther, M.J.; Statistics in Medicine 38(11), 2074-2102, 2019.The ADEMP reporting standard followed here, including Monte Carlo standard errors on all performance measures and the treatment of correlated results when competing methods are applied to the same replicate.
- A cautionary note for plasmode simulation studies in the setting of causal inference. Shaw, P.A., Gruber, S., Williamson, B.D., Desai, R., Shortreed, S.M., Krakauer, C., Nelson, J.C. and van der Laan, M.J.; Statistics in Medicine 45(18-19), 2026.Shows that a badly specified plasmode reference value can make estimators look spuriously biased with sub-nominal coverage, and motivates defining each estimand's reference value inside the resampling population itself.
- Efficiency of two sample tests via the restricted mean survival time for analyzing event time observations. Tian, L., Fu, H., Ruberg, S.J., Uno, H. and Wei, L.J.; Biometrics 74(2), 694-702, 2018.The reference analysis of RMST-based tests against the log-rank test, concluding that the log-rank test is more efficient under proportional hazards while RMST wins with early benefit. Motivates including an RMST comparator on the real-data empirical distribution.
- The robust inference for the Cox proportional hazards model. Lin, D.Y. and Wei, L.J.; Journal of the American Statistical Association 84(408), 1074-1078, 1989.Justifies treating the unadjusted Cox fit as an estimator of a well-defined limiting parameter when covariates are omitted, which is what makes the marginal estimand in this study meaningful.
- Levamisole and fluorouracil for adjuvant therapy of resected colon carcinoma. Moertel, C.G., Fleming, T.R., Macdonald, J.S. et al.; New England Journal of Medicine 322(6), 352-358, 1990.The primary report of the randomised trial whose patient records form the data-generating mechanism for this study.
Methods in use
- Cox proportional hazards partial likelihood with Breslow or Efron handling of tied event times; Wald test on the treatment coefficient.
- The log-rank test, equivalently the score test of the unadjusted Cox model, as the conventional primary analysis of a randomised survival trial.
- Parametric Monte Carlo simulation from Weibull or exponential conditional hazards with independent administrative censoring.
- Plasmode / real-data resampling designs in which records are bootstrapped from an actual study so that the covariate joint distribution and censoring pattern are real.
- Permutation or re-randomisation of the treatment label to construct an exactly null data-generating mechanism in a randomised comparison.
- Restricted mean survival time contrasts at a fixed restriction time, estimated from Kaplan-Meier curves with Greenwood-type variance.
- ADEMP performance measures: bias, empirical versus model-based standard error, confidence interval coverage and empirical rejection rate, each with a Monte Carlo standard error.
- Sandwich (robust) variance estimation for Cox models fitted under known misspecification.
Open gaps
- Whether the power gain from covariate adjustment in a Cox model survives when the data-generating mechanism is a real trial, in which the realised covariate imbalance is a fixed feature that can offset the non-collapsibility inflation instead of averaging out.
- Decomposing the adjusted-versus-unadjusted power difference into its two measurable ingredients, the ratio of estimands and the ratio of standard errors, whose relative size determines the sign of the difference.
- The type I error and power of per-analysis data-driven covariate selection in a survival trial, measured under a real-data null rather than asserted as a risk.
- Head-to-head efficiency of RMST-based tests against the log-rank test on a real trial's empirical survival distribution rather than under a parametric hazard specification.
- Routine reporting of paired Monte Carlo standard errors for differences in power between analysis strategies evaluated on the same replicates, without which small reported differences are indistinguishable from noise.
- Finite-sample bias of the Cox partial likelihood estimator at realistic trial sizes when the reference value is the parameter of the resampled real population rather than an assumed parametric truth.
Grounded search sources (18) · mode: live:agent:claude-opus-5
- Morris, T.P., White, I.R. and Crowther, M.J. (2019) Using simulation studies to evaluate statistical methods. Statistics in Medicine 38(11), 2074-2102.
- Kahan, B.C., Jairath, V., Dore, C.J. and Morris, T.P. (2014) The risks and rewards of covariate adjustment in randomized trials: an assessment of 12 outcomes from 8 studies. Trials 15, 139.
- Ford, I., Norrie, J. and Ahmadi, S. (1995) Model inconsistency, illustrated by the Cox proportional hazards model. Statistics in Medicine 14(8), 735-746.
- Robinson, L.D. and Jewell, N.P. (1991) Some surprising results about covariate adjustment in logistic regression models. International Statistical Review 59(2), 227-240.
- Hernandez, A.V., Eijkemans, M.J.C. and Steyerberg, E.W. (2006) Randomized controlled trials with time-to-event outcomes: how much does prespecified covariate adjustment increase power? Annals of Epidemiology 16(1), 41-48.
- Hernandez, A.V., Steyerberg, E.W. and Habbema, J.D.F. (2004) Covariate adjustment in randomized controlled trials with dichotomous outcomes increases statistical power and reduces sample size requirements. Journal of Clinical Epidemiology 57(5), 454-460.
- Momal, R., Li, H., Trichelair, P., Blum, M.G.B. and Balazard, F. (2023) More efficient and inclusive time-to-event trials with covariate adjustment: a simulation study. Trials 24, 380.
- Shaw, P.A., Gruber, S., Williamson, B.D., Desai, R., Shortreed, S.M., Krakauer, C., Nelson, J.C. and van der Laan, M.J. (2026) A cautionary note for plasmode simulation studies in the setting of causal inference. Statistics in Medicine 45(18-19).
- Shaw, P.A. et al. (2025) A cautionary note for plasmode simulation studies in the setting of causal inference (preprint). arXiv:2504.11740.
- Tian, L., Fu, H., Ruberg, S.J., Uno, H. and Wei, L.J. (2018) Efficiency of two sample tests via the restricted mean survival time for analyzing event time observations. Biometrics 74(2), 694-702.
- Eaton, A., Therneau, T. and Le-Rademacher, J. (2020) Designing clinical trials with (restricted) mean survival time endpoint: practical considerations. Clinical Trials 17(3), 285-294.
- Lin, D.Y. and Wei, L.J. (1989) The robust inference for the Cox proportional hazards model. Journal of the American Statistical Association 84(408), 1074-1078.
- Kahan, B.C. and Morris, T.P. (2021) Redressing the balance: covariate adjustment in randomised trials. BJOG 128(10), 1573.
- U.S. Food and Drug Administration (2023) Adjusting for Covariates in Randomized Clinical Trials for Drugs and Biological Products: Guidance for Industry.
- Moertel, C.G., Fleming, T.R., Macdonald, J.S. et al. (1990) Levamisole and fluorouracil for adjuvant therapy of resected colon carcinoma. New England Journal of Medicine 322(6), 352-358.
- Moertel, C.G., Fleming, T.R., Macdonald, J.S. et al. (1995) Fluorouracil plus levamisole as effective adjuvant therapy after resection of stage III colon carcinoma: a final report. Annals of Internal Medicine 122(5), 321-326.
- Therneau, T.M. and Grambsch, P.M. (2000) Modeling Survival Data: Extending the Cox Model. New York: Springer.
- Rdatasets archive mirror of the 'colon' dataset from the R survival package (data file actually retrieved for this study).
2.Ideation
3 candidate ideas generated; selected: “Does covariate adjustment really buy power in a randomised survival trial? A plasmode resampling study of the NCCTG adjuvant colon cancer trial”.
- Idea 1Selected
Does covariate adjustment really buy power in a randomised survival trial? A plasmode resampling study of the NCCTG adjuvant colon cancer trial
Hypothesis. In the resampling population defined by the observation and levamisole-plus-fluorouracil arms of the NCCTG colon trial, prespecified adjustment of the Cox model for eight baseline prognostic covariates yields strictly higher empirical power for the treatment comparison than the unadjusted log-rank test, at per-arm sizes of 150 and 300, with type I error at or below the nominal 5%. The hypothesis is refuted if the paired power difference (adjusted Cox minus log-rank) is zero or negative, judged against its discordance-based Monte Carlo standard error, at either sample size. Mechanistically, adjustment gains power only if the ratio of the mean adjusted to mean unadjusted log hazard ratio exceeds the ratio of their standard errors; the study measures both ratios directly, so the direction of the result is explained rather than merely reported.
Dataset. Adjuvant chemotherapy trial in stage B/C colon cancer colon_cancer_trial
Test plan, novelty and risk
Test plan. Sample: dataset colon_cancer_trial, death records only (etype == 2, one row per patient), arms 'Obs' and 'Lev+5FU' only, complete cases on the prespecified covariates (nodes and differ each have a few missing values); the analysed set is reported exactly. Prespecified covariates: age (years), sex, obstruct, adhere, surg, log(nodes+1), differ (1-3), extent (1-4); perfor is excluded a priori as too rare (about 3%) for stable estimation in replicates. Data-generating mechanisms, all resampled from this observed sample with a fixed seed: (D1) as-randomised bootstrap, n patients drawn with replacement within each arm, preserving the trial's real treatment effect, real covariate joint distribution, real censoring pattern and its realised chance imbalance; (D2) risk-balanced bootstrap, identical except that draws are stratified on tertiles of a baseline prognostic index computed from a covariate-only Cox model so that the two arms have identical strata proportions, removing first-order chance imbalance; (D3) permutation null, 2n patients drawn with replacement from the pooled two arms and randomly split into two arms of n, giving an exactly null treatment effect with the real covariate-outcome relationship intact. Sizes n = 150 and n = 300 per arm. Methods applied to every replicate: M1 log-rank test; M2 unadjusted Cox Wald test; M3 Cox adjusted for all eight prespecified covariates; M4 Cox with the covariate set chosen inside the replicate by backward elimination from the eight at p > 0.10; M5 Kaplan-Meier restricted mean survival time difference at tau = 5 years with Greenwood-type variance. Estimator: Breslow partial likelihood by Newton-Raphson, validated against statsmodels PHReg on the full sample before use. Estimands and reference values are defined inside each resampling population by fitting the same estimators to one very large draw (10,000 per arm) from that population. Performance measures over 2,000 replicates per cell: empirical rejection rate at alpha = 0.05 (power under D1 and D2, type I error under D3) with Monte Carlo standard error; paired power differences with discordance-based Monte Carlo standard errors; mean estimate and bias against the reference; empirical versus mean model-based standard error; 95% confidence interval coverage; mean number of covariates retained by M4; and non-convergence counts. Refutation criterion as stated in the hypothesis; type I error is judged inflated if it exceeds 5% by more than two Monte Carlo standard errors.
Novelty. The claim that Cox adjustment increases power rests almost entirely on parametric simulation (Hernandez et al. 2006; Momal et al. 2023) in which covariates are balanced in expectation by construction. No published study resamples an actual randomised survival trial and decomposes the adjusted-versus-unadjusted power difference into its two measurable ingredients, the estimand ratio and the standard-error ratio, nor measures the type I error of per-replicate data-driven selection against a real-data null. The paired D1/D2 contrast isolates the part of the answer that is an artefact of one trial's realised imbalance from the part that is genuine non-collapsibility.
Expected outcome. On the full sample the unadjusted and adjusted treatment coefficients are already known to be nearly identical while the adjusted standard error is larger, so under D1 adjustment is expected to lose a small amount of power, refuting the primary hypothesis; under D2, with imbalance removed, the non-collapsibility inflation should reappear and adjustment should gain power. Data-driven selection (M4) is expected to show mild type I error inflation. RMST at 5 years is expected to be close to but slightly below the log-rank test in power, consistent with approximately proportional hazards.
Main risk. The two candidate failure modes are computational and interpretive. Computationally, bootstrap replicates create heavy ties and can occasionally produce near-separated covariate patterns, so the Cox fitter needs step control and a recorded non-convergence count. Interpretively, the finding under D1 is specific to this trial's realised imbalance; the D2 arm of the design and explicit reporting of the imbalance magnitude are what keep the conclusion honest and generalisable as a warning rather than as a universal law.
- Idea 2
Cause-specific versus composite endpoints under a competing risk: how much power does treating liver transplantation as censoring cost in the Mayo PBC trial?
Hypothesis. In the randomised 312-patient stratum of the Mayo PBC trial, the conventional overall-survival analysis that censors at transplantation has lower empirical power to detect a treatment effect than the composite transplant-free survival analysis, because transplantation is informatively related to disease severity; the loss is at least 3 percentage points at a per-arm size of 120. Refuted if the two rejection rates differ by less than their paired Monte Carlo standard error, or if the ordering is reversed.
Dataset. Mayo Clinic primary biliary cholangitis trial mayo_pbc_trial
Test plan, novelty and risk
Test plan. Sample: dataset mayo_pbc_trial restricted to the 312 randomised patients with non-missing trt. Two outcome definitions from the three-level status column: (a) death-only, status == 2 as the event and status in {0,1} as censoring; (b) composite transplant-free survival, status in {1,2} as the event. Resampling from the observed sample: bootstrap within arm at n = 80 and n = 120 per arm for power, and a permutation-null construction (pooled draw with random arm labels) for type I error, 2,000 replicates each with a fixed seed. Methods: log-rank test and Cox Wald test for each outcome definition, plus a Fine-Gray-style comparison of cumulative incidence of death with transplant as a competing event, implemented as a Cox model on the subdistribution risk set constructed by hand. Performance measures: rejection rate at alpha = 0.05 with Monte Carlo standard error, paired differences with discordance-based standard errors, mean log hazard ratio and bias relative to the reference value computed on a large draw from the resampling population, and the empirical proportion of transplants occurring before death in each arm.
Novelty. The Mayo PBC data are the standard teaching example of a competing risk, but the practical question a trialist faces - what the choice of endpoint costs in power when the competing event is neither rare nor independent - is normally answered by parametric simulation rather than by resampling the trial itself. The design measures the cost directly in the trial's own empirical distribution.
Expected outcome. D-penicillamine is known to have no meaningful effect in this trial, so power under either definition may be near the nominal level, which would make the power comparison uninformative and turn the study into a type I error and bias comparison. That risk is stated explicitly and is the reason this idea ranks second.
Main risk. With only 25 transplants and a treatment that is genuinely ineffective, both arms of the comparison may sit at the null, so there may be no power difference to measure. The study would then reduce to a much weaker statement about type I error and estimand differences.
- Idea 3
How many events does the Grambsch-Therneau test need? Empirical power to detect a real non-proportional hazard in the Rotterdam breast cancer cohort
Hypothesis. The number of positive lymph nodes has a hazard ratio that attenuates over follow-up in the Rotterdam cohort. The global Grambsch-Therneau score test based on scaled Schoenfeld residuals requires at least 400 deaths to detect that departure with 80% power; below 250 deaths its power is under 50%, so the proportional-hazards checks reported in trial-sized survival analyses are largely uninformative. Refuted if power exceeds 80% at 250 deaths or falls below 50% at 400 deaths.
Dataset. Rotterdam tumour bank breast cancer cohort rotterdam_breast_cancer
Test plan, novelty and risk
Test plan. Sample: dataset rotterdam_breast_cancer, overall survival outcome (dtime, death), complete cases on age, meno, size, grade, nodes, log(pgr+1), log(er+1), hormon and chemo. Resampling from the observed cohort: bootstrap subsamples of n patients with replacement, with n chosen so that the expected number of deaths is 100, 250, 400 and 700, 2,000 replicates per cell with a fixed seed. A null comparator is built by permuting each patient's observed event time and status jointly across patients within strata of the covariate of interest, which breaks the time-varying part of the association while preserving the marginal distributions. Methods: Cox model with all covariates, followed by the Grambsch-Therneau test for log(nodes+1) and the global test, both implemented from scaled Schoenfeld residuals; a comparator based on splitting follow-up at the median event time and testing the treatment-by-period interaction. Performance measures: empirical rejection rate with Monte Carlo standard error at each event count, the estimated time-varying coefficient slope and its bias against a reference value from a large draw, and coverage.
Novelty. Proportional-hazards diagnostics are reported almost universally and interpreted as if a non-significant result licenses the Cox model, but their power at realistic trial event counts is rarely quantified against a real, documented departure. Using a cohort where the departure genuinely exists avoids assuming a parametric form for the violation.
Expected outcome. Power is expected to rise steeply with event count, with the practical recommendation that a non-significant proportional-hazards test in a trial with fewer than 250 deaths carries almost no evidential weight.
Main risk. The cohort is observational, so the departure being detected is a covariate effect rather than a treatment effect, which weakens the direct read-across to randomised trials. Building a genuinely null comparator that preserves the marginal hazard while removing only the time-varying component is also delicate and could bias the type I error assessment.
Ranking rationale
Idea 1 is ranked first because it is the only one whose answer is genuinely uncertain in a way the data can settle sharply, and because the preliminary full-sample fit already shows the two ingredients of the answer pulling in opposite directions, so both a supported and a refuted result would be publishable and would carry a concrete recommendation. It uses a genuinely randomised trial with a real, moderate treatment effect, a strongly prognostic covariate set and a long, realistic censoring pattern; the analysis is three cheap resampling designs over the same 594 records and runs comfortably inside the time budget. Idea 2 is ranked second because the Mayo PBC treatment is known to be ineffective, so the power contrast it proposes to measure may not exist, leaving only a weaker statement. Idea 3 is ranked third because the Rotterdam data are observational, so its conclusion about trial practice is an analogy rather than a direct measurement, and because constructing an honest null for a time-varying effect is the hardest part of the design and the most likely to fail.
3.Experimentation
Experiment succeeded in 1 round (50.706s).
Data provenance
Adjuvant chemotherapy trial in stage B/C colon cancer
- Source
- R package 'survival', mirrored by the Rdatasets archive; original data from a North Central Cancer Treatment Group / Mayo Clinic trial
- Citation
- Moertel, C.G., Fleming, T.R., Macdonald, J.S. et al. (1990) Levamisole and fluorouracil for adjuvant therapy of resected colon carcinoma. New England Journal of Medicine 322(6), 352-358. DOI: 10.1056/NEJM199002083220602. Final report: Moertel, C.G. et al. (1995) Fluorouracil plus levamisole as effective adjuvant therapy after resection of stage III colon carcinoma. Annals of Internal Medicine 122(5), 321-326. DOI: 10.7326/0003-4819-122-5-199503010-00001. See also Laurie, J.A. et al. (1989) Journal of Clinical Oncology 7(10), 1447-1456. (The survival package's own help file misprints these two as volume 332 and as a 1991 stage-II report; the corrected forms are given here.) Data: colon, R package 'survival'.
- Licence
- Redistributed in the R package 'survival' under LGPL (>= 2) via the Rdatasets archive. The package's COPYRIGHTS file reads "Copyright 2000 Mayo Foundation for Medical Education and Research. This software is accepted by users 'as is' and without warranties or guarantees of any kind." No per-dataset licence is stated.
- File
- https://vincentarelbundock.github.io/Rdatasets/csv/survival/colon.csv
- Observations
- 1,858
- Variables (16)
- id, study, rx, sex, age, obstruct, perfor, adhere, nodes, status, differ, extent, surg, node4, time, etype
- Units
- The table is in long form: etype is 1 for the recurrence record and 2 for the death record, so 1,858 rows are 929 patients. time is days to the event or censoring and status is the 0/1 event indicator. rx is the randomised arm ('Obs', 'Lev', 'Lev+5FU'). nodes is the number of positive lymph nodes, differ is tumour differentiation (1 well to 3 poor), extent is depth of invasion (1 submucosa to 4 contiguous structures), and obstruct, perfor, adhere, surg and node4 are 0/1 clinical indicators.
- Retrieved
- 8 Sept 2026, 15:47
- Checksum
- sha256 6f3472a64f696e31…
Real, publicly available data — downloaded and checksum-verified before the analysis ran, never simulated.
Result (RESULT_JSON)
{
"n_two_arm_death_records": 619,
"n_dropped_missing_covariates": 25,
"n_analysed": 594,
"n_treated": 289,
"n_control": 305,
"n_deaths": 281,
"pct_censored": 52.69360269360269,
"median_followup_days": 2003.5,
"max_followup_days": 3309,
"deaths_within_tau1": 251,
"at_risk_tau1_trt": 178,
"at_risk_tau1_ctl": 153,
"at_risk_tau2_trt": 85,
"at_risk_tau2_ctl": 64,
"tau1_days": 1826.25,
"tau2_days": 2374.125,
"n_replicates": 4000,
"sizes_per_arm": [
150,
300
],
"alpha": 0.05,
"seed": 20260908,
"validation_cox_max_abs_diff": 8.658906924807752e-13,
"validation_logrank_chi2_diff": 2.131628207280301e-14,
"full_unadj_loghr": -0.38545353836572704,
"full_unadj_se": 0.121136548275686,
"full_adj_loghr": -0.38519775637782255,
"full_adj_se": 0.12280857102538471,
"full_unadj_hr": 0.6801420957078507,
"full_adj_hr": 0.6803160860559982,
"full_unadj_z": -3.1819755792322963,
"full_adj_z": -3.1365706250111938,
"full_logrank_chi2": 10.252674536403129,
"full_logrank_p": 0.0013648657626475571,
"prognostic_index_gap_trt_minus_ctl": -0.07199092956663256,
"cov_beta": {
"age": 0.0022784614856900124,
"sex": -0.0990827136545213,
"obstruct": 0.10937732383517648,
"adhere": 0.2564139721660925,
"surg": 0.2929150185321035,
"lognodes": 0.7804652150893122,
"differ": 0.11949259831831624,
"extent": 0.3590994545166239
},
"reference_values": {
"D1": {
"M1": -0.3853849005364278,
"M2": -0.3853849005364278,
"M3": -0.38458033438909456,
"M4": -0.38458033438909456,
"M5": 119.10587529929307,
"M6": 187.55249223559235
},
"D2": {
"M1": -0.32429677208872837,
"M2": -0.32429677208872837,
"M3": -0.380924296575542,
"M4": -0.380924296575542,
"M5": 91.64163672031508,
"M6": 149.65296819209675
},
"D3": {
"M1": 0,
"M2": 0,
"M3": 0,
"M4": 0,
"M5": 0,
"M6": 0
}
},
"cells": {
"D1_n150": {
"M1": {
"reject_pct": 63.425,
"reject_mcse": 0.7615394564630253,
"fail_n": 0,
"mean_est": null,
"bias": null,
"emp_se": null,
"mod_se": null,
"rmse": null,
"coverage_pct": null
},
"M2": {
"reject_pct": 62.875,
"reject_mcse": 0.763910069150813,
"fail_n": 0,
"mean_est": -0.3887879900896369,
"bias": -0.00340308955320906,
"emp_se": 0.17449155086417387,
"mod_se": 0.1707516081841506,
"rmse": 0.17450292409057502,
"coverage_pct": 93.75
},
"M3": {
"reject_pct": 58.35,
"reject_mcse": 0.7794673662187532,
"fail_n": 0,
"mean_est": -0.3832890166079713,
"bias": 0.0012913177811232357,
"emp_se": 0.18741532484014004,
"mod_se": 0.1767192112994987,
"rmse": 0.18739634564353388,
"coverage_pct": 93.375
},
"M4": {
"reject_pct": 60.12499999999999,
"reject_mcse": 0.7741906055681637,
"fail_n": 0,
"mean_est": -0.3823452723294749,
"bias": 0.002235062059619708,
"emp_se": 0.1859092293321397,
"mod_se": 0.1731294201976376,
"rmse": 0.1858994257465384,
"coverage_pct": 93.125,
"mean_covariates_kept": 3.0315
},
"M5": {
"reject_pct": 42.575,
"reject_mcse": 0.7818039036420834,
"fail_n": 0,
"mean_est": 119.5582788574004,
"bias": 0.4524035581073349,
"emp_se": 68.32925037588201,
"mod_se": 66.98421444092075,
"rmse": 68.32220652390501,
"coverage_pct": 94.35
},
"M6": {
"reject_pct": 51.3,
"reject_mcse": 0.7903021574056344,
"fail_n": 0,
"mean_est": 188.30302281517504,
"bias": 0.7505305795826871,
"emp_se": 95.95881822146228,
"mod_se": 93.95521971017808,
"rmse": 95.949758034671,
"coverage_pct": 94.225
},
"se_ratio_M3_over_M2": 1.0349490302247235,
"estimand_ratio_M3_over_M2": 0.9858561127868232,
"rmst_tau1_extrapolated_arms": 0
},
"D1_n300": {
"M1": {
"reject_pct": 89.45,
"reject_mcse": 0.4857204700236547,
"fail_n": 0,
"mean_est": null,
"bias": null,
"emp_se": null,
"mod_se": null,
"rmse": null,
"coverage_pct": null
},
"M2": {
"reject_pct": 89.35,
"reject_mcse": 0.4877441696217394,
"fail_n": 0,
"mean_est": -0.38802018097232654,
"bias": -0.0026352804358987236,
"emp_se": 0.1236519415721068,
"mod_se": 0.12056104022166585,
"rmse": 0.12366456609257116,
"coverage_pct": 94.4
},
"M3": {
"reject_pct": 86.75,
"reject_mcse": 0.5360591152102536,
"fail_n": 0,
"mean_est": -0.3849120873743397,
"bias": -0.0003317529852451262,
"emp_se": 0.13032644829252552,
"mod_se": 0.1234299550656652,
"rmse": 0.1303105787678284,
"coverage_pct": 93.55
},
"M4": {
"reject_pct": 86.8,
"reject_mcse": 0.5352008968602351,
"fail_n": 0,
"mean_est": -0.3829418855356665,
"bias": 0.001638448853428065,
"emp_se": 0.13028067268701288,
"mod_se": 0.12194649775623254,
"rmse": 0.130274690277916,
"coverage_pct": 93.475,
"mean_covariates_kept": 3.8685
},
"M5": {
"reject_pct": 70.75,
"reject_mcse": 0.7192769807243937,
"fail_n": 0,
"mean_est": 120.15950979105364,
"bias": 1.053634491760576,
"emp_se": 48.51888210522098,
"mod_se": 47.43326705138104,
"rmse": 48.524257293670914,
"coverage_pct": 94.6
},
"M6": {
"reject_pct": 80.65,
"reject_mcse": 0.624615381654983,
"fail_n": 0,
"mean_est": 188.89786550110725,
"bias": 1.345373265514894,
"emp_se": 68.07736718030593,
"mod_se": 66.52983613929197,
"rmse": 68.08215125453371,
"coverage_pct": 94.625
},
"se_ratio_M3_over_M2": 1.023796367704895,
"estimand_ratio_M3_over_M2": 0.9919898661193384,
"rmst_tau1_extrapolated_arms": 0
},
"D2_n150": {
"M1": {
"reject_pct": 46.825,
"reject_mcse": 0.7889739182951487,
"fail_n": 0,
"mean_est": null,
"bias": null,
"emp_se": null,
"mod_se": null,
"rmse": null,
"coverage_pct": null
},
"M2": {
"reject_pct": 46.35,
"reject_mcse": 0.7884601289856069,
"fail_n": 0,
"mean_est": -0.32227104874351503,
"bias": 0.002025723345213337,
"emp_se": 0.16186604484766798,
"mod_se": 0.17041385049040536,
"rmse": 0.16185848719044654,
"coverage_pct": 96.025
},
"M3": {
"reject_pct": 56.375,
"reject_mcse": 0.7841172385236789,
"fail_n": 0,
"mean_est": -0.37720961176831014,
"bias": 0.003714684807231861,
"emp_se": 0.1857295742941298,
"mod_se": 0.17639951969945045,
"rmse": 0.1857435053561871,
"coverage_pct": 93.325
},
"M4": {
"reject_pct": 56.89999999999999,
"reject_mcse": 0.7830054278228218,
"fail_n": 0,
"mean_est": -0.37411480671447545,
"bias": 0.006809489861066553,
"emp_se": 0.18448373112687472,
"mod_se": 0.17293063691104746,
"rmse": 0.1845863148797829,
"coverage_pct": 93.1,
"mean_covariates_kept": 3.038
},
"M5": {
"reject_pct": 25.725,
"reject_mcse": 0.6911447704714259,
"fail_n": 0,
"mean_est": 90.00626352160569,
"bias": -1.6353731987094022,
"emp_se": 64.14753036563985,
"mod_se": 67.1821724603305,
"rmse": 64.16035669396437,
"coverage_pct": 96.125
},
"M6": {
"reject_pct": 34.2,
"reject_mcse": 0.750059997600192,
"fail_n": 0,
"mean_est": 147.45025895579684,
"bias": -2.20270923629991,
"emp_se": 89.61491879193623,
"mod_se": 94.19866071161076,
"rmse": 89.63078650579546,
"coverage_pct": 95.925
},
"se_ratio_M3_over_M2": 1.03512431173769,
"estimand_ratio_M3_over_M2": 1.1704731568007491,
"rmst_tau1_extrapolated_arms": 0
},
"D2_n300": {
"M1": {
"reject_pct": 79.175,
"reject_mcse": 0.6420318089861281,
"fail_n": 0,
"mean_est": null,
"bias": null,
"emp_se": null,
"mod_se": null,
"rmse": null,
"coverage_pct": null
},
"M2": {
"reject_pct": 78.95,
"reject_mcse": 0.6445730175860607,
"fail_n": 0,
"mean_est": -0.3259531557196058,
"bias": -0.0016563836308774449,
"emp_se": 0.1125568425373474,
"mod_se": 0.12026141897503616,
"rmse": 0.1125549605659802,
"coverage_pct": 95.925
},
"M3": {
"reject_pct": 87.575,
"reject_mcse": 0.5215648030206793,
"fail_n": 0,
"mean_est": -0.3819658931839065,
"bias": -0.001041596608364448,
"emp_se": 0.12553690316352545,
"mod_se": 0.12324253936208508,
"rmse": 0.12552553166933392,
"coverage_pct": 94.325
},
"M4": {
"reject_pct": 87.1,
"reject_mcse": 0.5299976415041864,
"fail_n": 0,
"mean_est": -0.37810230217648694,
"bias": 0.002821994399055061,
"emp_se": 0.12565721598216165,
"mod_se": 0.12185300179683038,
"rmse": 0.12567319581674455,
"coverage_pct": 94.1,
"mean_covariates_kept": 3.90325
},
"M5": {
"reject_pct": 47.7,
"reject_mcse": 0.7897325496647585,
"fail_n": 0,
"mean_est": 91.60145493900984,
"bias": -0.0401817813052503,
"emp_se": 43.28997813171,
"mod_se": 47.55454942772805,
"rmse": 43.284585196901425,
"coverage_pct": 96.875
},
"M6": {
"reject_pct": 62.325,
"reject_mcse": 0.7661746496393625,
"fail_n": 0,
"mean_est": 149.8902329192324,
"bias": 0.23726472713565272,
"emp_se": 60.845310611180125,
"mod_se": 66.67981676602656,
"rmse": 60.83816713187445,
"coverage_pct": 96.925
},
"se_ratio_M3_over_M2": 1.0247886679905858,
"estimand_ratio_M3_over_M2": 1.171842905894381,
"rmst_tau1_extrapolated_arms": 0
},
"D3_n150": {
"M1": {
"reject_pct": 5.125,
"reject_mcse": 0.3486525401456298,
"fail_n": 0,
"mean_est": null,
"bias": null,
"emp_se": null,
"mod_se": null,
"rmse": null,
"coverage_pct": null
},
"M2": {
"reject_pct": 5.025,
"reject_mcse": 0.34541618918342554,
"fail_n": 0,
"mean_est": 0.00032759958267066105,
"bias": 0.00032759958267066105,
"emp_se": 0.1716114973626993,
"mod_se": 0.16840713139062702,
"rmse": 0.1715903573107986,
"coverage_pct": 94.975
},
"M3": {
"reject_pct": 6.35,
"reject_mcse": 0.3855766784959899,
"fail_n": 0,
"mean_est": -0.0008802383648052618,
"bias": -0.0008802383648052618,
"emp_se": 0.18524471712503995,
"mod_se": 0.17240033411052616,
"rmse": 0.18522365167820568,
"coverage_pct": 93.65
},
"M4": {
"reject_pct": 6.05,
"reject_mcse": 0.3769607075014583,
"fail_n": 0,
"mean_est": -0.0010686083625882938,
"bias": -0.0010686083625882938,
"emp_se": 0.18273368031461523,
"mod_se": 0.17007904598463067,
"rmse": 0.18271396209834723,
"coverage_pct": 93.95,
"mean_covariates_kept": 3.1085
},
"M5": {
"reject_pct": 4.9750000000000005,
"reject_mcse": 0.34378386196853394,
"fail_n": 0,
"mean_est": -0.3955846457118599,
"bias": -0.3955846457118599,
"emp_se": 67.96503835454018,
"mod_se": 67.32303707337014,
"rmse": 67.95769356104141,
"coverage_pct": 95.025
},
"M6": {
"reject_pct": 4.8,
"reject_mcse": 0.33799408278844173,
"fail_n": 0,
"mean_est": -0.39495867705827026,
"bias": -0.39495867705827026,
"emp_se": 95.80900222707282,
"mod_se": 94.55817359722134,
"rmse": 95.79783953136362,
"coverage_pct": 95.2
},
"se_ratio_M3_over_M2": 1.0237116010879417,
"estimand_ratio_M3_over_M2": null,
"rmst_tau1_extrapolated_arms": 0
},
"D3_n300": {
"M1": {
"reject_pct": 5.2,
"reject_mcse": 0.35105555115964193,
"fail_n": 0,
"mean_est": null,
"bias": null,
"emp_se": null,
"mod_se": null,
"rmse": null,
"coverage_pct": null
},
"M2": {
"reject_pct": 5.025,
"reject_mcse": 0.34541618918342554,
"fail_n": 0,
"mean_est": -0.0012808778731005237,
"bias": -0.0012808778731005237,
"emp_se": 0.11804314893440568,
"mod_se": 0.118939540846324,
"rmse": 0.11803534264034436,
"coverage_pct": 94.975
},
"M3": {
"reject_pct": 6.45,
"reject_mcse": 0.3883933251228708,
"fail_n": 0,
"mean_est": -0.0010016082755634392,
"bias": -0.0010016082755634392,
"emp_se": 0.12535059982403912,
"mod_se": 0.12035764461903306,
"rmse": 0.12533893210876262,
"coverage_pct": 93.55
},
"M4": {
"reject_pct": 6.1,
"reject_mcse": 0.37841445532643175,
"fail_n": 0,
"mean_est": -0.0009909648594416454,
"bias": -0.0009909648594416454,
"emp_se": 0.12428763621253686,
"mod_se": 0.11967578734096854,
"rmse": 0.12427605027729086,
"coverage_pct": 93.9,
"mean_covariates_kept": 3.882
},
"M5": {
"reject_pct": 4.925,
"reject_mcse": 0.3421419204803761,
"fail_n": 0,
"mean_est": 0.3194782048641011,
"bias": 0.3194782048641011,
"emp_se": 47.709179188543665,
"mod_se": 47.71518203613719,
"rmse": 47.704284961869405,
"coverage_pct": 95.075
},
"M6": {
"reject_pct": 5.3,
"reject_mcse": 0.35422803389906904,
"fail_n": 0,
"mean_est": 0.4954743313550334,
"bias": 0.4954743313550334,
"emp_se": 66.91930071151113,
"mod_se": 67.00980410066185,
"rmse": 66.91276974037734,
"coverage_pct": 94.7
},
"se_ratio_M3_over_M2": 1.0119228959740254,
"estimand_ratio_M3_over_M2": null,
"rmst_tau1_extrapolated_arms": 0
}
},
"paired_differences": {
"D1_n150_M3_minus_M1": {
"diff_pct": -5.074999999999996,
"mcse_pct": 0.6167950176111997,
"discordant_a_only": 208,
"discordant_c_only": 411
},
"D1_n150_M4_minus_M1": {
"diff_pct": -3.300000000000003,
"mcse_pct": 0.6111689619082435,
"discordant_a_only": 235,
"discordant_c_only": 367
},
"D1_n150_M2_minus_M1": {
"diff_pct": -0.5499999999999949,
"mcse_pct": 0.11693748329770055,
"discordant_a_only": 0,
"discordant_c_only": 22
},
"D1_n150_M5_minus_M1": {
"diff_pct": -20.85,
"mcse_pct": 0.7009417771826701,
"discordant_a_only": 63,
"discordant_c_only": 897
},
"D1_n150_M6_minus_M1": {
"diff_pct": -12.124999999999996,
"mcse_pct": 0.5810517134902883,
"discordant_a_only": 57,
"discordant_c_only": 542
},
"D1_n150_M3_minus_M2": {
"diff_pct": -4.525000000000001,
"mcse_pct": 0.616851759947234,
"discordant_a_only": 218,
"discordant_c_only": 399
},
"D1_n300_M3_minus_M1": {
"diff_pct": -2.6999999999999913,
"mcse_pct": 0.4265296003796219,
"discordant_a_only": 93,
"discordant_c_only": 201
},
"D1_n300_M4_minus_M1": {
"diff_pct": -2.649999999999997,
"mcse_pct": 0.4295280840643601,
"discordant_a_only": 96,
"discordant_c_only": 202
},
"D1_n300_M2_minus_M1": {
"diff_pct": -0.10000000000000007,
"mcse_pct": 0.049974993746873045,
"discordant_a_only": 0,
"discordant_c_only": 4
},
"D1_n300_M5_minus_M1": {
"diff_pct": -18.699999999999996,
"mcse_pct": 0.6335041436328573,
"discordant_a_only": 17,
"discordant_c_only": 765
},
"D1_n300_M6_minus_M1": {
"diff_pct": -8.799999999999997,
"mcse_pct": 0.47369821616721336,
"discordant_a_only": 19,
"discordant_c_only": 371
},
"D1_n300_M3_minus_M2": {
"diff_pct": -2.599999999999991,
"mcse_pct": 0.42668489544393295,
"discordant_a_only": 95,
"discordant_c_only": 199
},
"D2_n150_M3_minus_M1": {
"diff_pct": 9.549999999999995,
"mcse_pct": 0.5609807260503699,
"discordant_a_only": 461,
"discordant_c_only": 79
},
"D2_n150_M4_minus_M1": {
"diff_pct": 10.074999999999996,
"mcse_pct": 0.5536231513854889,
"discordant_a_only": 467,
"discordant_c_only": 64
},
"D2_n150_M2_minus_M1": {
"diff_pct": -0.4749999999999977,
"mcse_pct": 0.10871335589521648,
"discordant_a_only": 0,
"discordant_c_only": 19
},
"D2_n150_M5_minus_M1": {
"diff_pct": -21.1,
"mcse_pct": 0.6972786387090888,
"discordant_a_only": 56,
"discordant_c_only": 900
},
"D2_n150_M6_minus_M1": {
"diff_pct": -12.624999999999996,
"mcse_pct": 0.5848310386342367,
"discordant_a_only": 53,
"discordant_c_only": 558
},
"D2_n150_M3_minus_M2": {
"diff_pct": 10.024999999999997,
"mcse_pct": 0.5628053337966867,
"discordant_a_only": 474,
"discordant_c_only": 73
},
"D2_n300_M3_minus_M1": {
"diff_pct": 8.400000000000007,
"mcse_pct": 0.484623565254518,
"discordant_a_only": 370,
"discordant_c_only": 34
},
"D2_n300_M4_minus_M1": {
"diff_pct": 7.925000000000004,
"mcse_pct": 0.4742611029274908,
"discordant_a_only": 351,
"discordant_c_only": 34
},
"D2_n300_M2_minus_M1": {
"diff_pct": -0.22499999999999745,
"mcse_pct": 0.07491557748559374,
"discordant_a_only": 0,
"discordant_c_only": 9
},
"D2_n300_M5_minus_M1": {
"diff_pct": -31.475,
"mcse_pct": 0.7461273978014746,
"discordant_a_only": 14,
"discordant_c_only": 1273
},
"D2_n300_M6_minus_M1": {
"diff_pct": -16.849999999999998,
"mcse_pct": 0.6136117461392017,
"discordant_a_only": 21,
"discordant_c_only": 695
},
"D2_n300_M3_minus_M2": {
"diff_pct": 8.625000000000005,
"mcse_pct": 0.48813660357526967,
"discordant_a_only": 378,
"discordant_c_only": 33
},
"D3_n150_M3_minus_M1": {
"diff_pct": 1.2250000000000003,
"mcse_pct": 0.34496353974007166,
"discordant_a_only": 120,
"discordant_c_only": 71
},
"D3_n150_M4_minus_M1": {
"diff_pct": 0.9250000000000002,
"mcse_pct": 0.3378773353600386,
"discordant_a_only": 110,
"discordant_c_only": 73
},
"D3_n150_M2_minus_M1": {
"diff_pct": -0.0999999999999994,
"mcse_pct": 0.049974993746873045,
"discordant_a_only": 0,
"discordant_c_only": 4
},
"D3_n150_M5_minus_M1": {
"diff_pct": -0.14999999999999944,
"mcse_pct": 0.32595149178980604,
"discordant_a_only": 82,
"discordant_c_only": 88
},
"D3_n150_M6_minus_M1": {
"diff_pct": -0.3249999999999996,
"mcse_pct": 0.27721578914268213,
"discordant_a_only": 55,
"discordant_c_only": 68
},
"D3_n150_M3_minus_M2": {
"diff_pct": 1.3249999999999995,
"mcse_pct": 0.3448711262921267,
"discordant_a_only": 122,
"discordant_c_only": 69
},
"D3_n300_M3_minus_M1": {
"diff_pct": 1.2500000000000004,
"mcse_pct": 0.3582727661991629,
"discordant_a_only": 128,
"discordant_c_only": 78
},
"D3_n300_M4_minus_M1": {
"diff_pct": 0.9000000000000001,
"mcse_pct": 0.34791881236863287,
"discordant_a_only": 115,
"discordant_c_only": 79
},
"D3_n300_M2_minus_M1": {
"diff_pct": -0.17499999999999946,
"mcse_pct": 0.06608588162383855,
"discordant_a_only": 0,
"discordant_c_only": 7
},
"D3_n300_M5_minus_M1": {
"diff_pct": -0.2749999999999996,
"mcse_pct": 0.33444894042289924,
"discordant_a_only": 84,
"discordant_c_only": 95
},
"D3_n300_M6_minus_M1": {
"diff_pct": 0.10000000000000007,
"mcse_pct": 0.28722378035253276,
"discordant_a_only": 68,
"discordant_c_only": 64
},
"D3_n300_M3_minus_M2": {
"diff_pct": 1.4249999999999998,
"mcse_pct": 0.35548184728618704,
"discordant_a_only": 130,
"discordant_c_only": 73
}
},
"hypothesis_supported": false
}Experiment script · experiment_v1.py
"""Plasmode resampling study of covariate adjustment in a randomised survival trial.
Real data: NCCTG/Mayo adjuvant colon cancer trial (`colon`), death records only,
observation vs levamisole+fluorouracil arms. Nothing is simulated: every replicate
is a bootstrap draw from the observed patient records, and every reference value is
the exact parameter of the resampling population (a weighted fit to those records).
"""
import json
import numpy as np
from scipy import stats
from statsmodels.duration.hazard_regression import PHReg
from statsmodels.duration.survfunc import survdiff
from dataset import load
SEED = 20260908
ALPHA = 0.05
ZCRIT = float(stats.norm.ppf(1 - ALPHA / 2))
TAU1 = 5 * 365.25 # conventional 5-year restriction time (days)
TAU2 = 6.5 * 365.25 # longer restriction time (days)
NREP = 4000 # replicates per (DGM, sample size) cell
SIZES = (150, 300) # patients per arm
COVS = ["age", "sex", "obstruct", "adhere", "surg", "lognodes", "differ", "extent"]
METHODS = ("M1", "M2", "M3", "M4", "M5", "M6")
def revcum(a):
return np.cumsum(a[::-1], axis=0)[::-1]
def cox_fit(X, t, d, wt=None, maxit=40, tol=1e-8):
"""Breslow partial-likelihood Cox fit by Newton-Raphson, with optional case
weights. Returns (beta, se) or None if it does not converge."""
n, p = X.shape
o = np.argsort(t, kind="mergesort")
X = np.ascontiguousarray(X[o]); t = t[o]; d = d[o]
w = np.ones(n) if wt is None else np.asarray(wt, float)[o]
uniq, first = np.unique(t, return_index=True)
ev = d.astype(bool)
if int(ev.sum()) < p + 1:
return None
i0 = first[np.searchsorted(uniq, t)][ev]; we = w[ev]
xsum = (X[ev] * we[:, None]).sum(0)
beta = np.zeros(p); H = None
for _ in range(maxit):
eta = X @ beta
eta -= eta.max()
ww = w * np.exp(eta)
wx = X * ww[:, None]
S0 = revcum(ww)[i0]
S1 = revcum(wx)[i0]
S2 = revcum(wx[:, :, None] * X[:, None, :])[i0]
xbar = S1 / S0[:, None]
grad = xsum - (we[:, None] * xbar).sum(0)
H = (np.einsum("i,ijk->jk", we, S2 / S0[:, None, None])
- np.einsum("i,ij,ik->jk", we, xbar, xbar))
try:
step = np.linalg.solve(H, grad)
except np.linalg.LinAlgError:
return None
m = float(np.max(np.abs(step)))
if not np.isfinite(m):
return None
if m > 5.0:
step = step * (5.0 / m)
beta = beta + step
if m < tol:
break
else:
return None
try:
var = np.diag(np.linalg.inv(H))
except np.linalg.LinAlgError:
return None
return None if (np.any(var <= 0) or not np.all(np.isfinite(beta))) else (beta, np.sqrt(var))
def logrank(t, d, g):
"""Two-sample log-rank test; g is a 0/1 group indicator. Returns (chi2, p)."""
o = np.argsort(t, kind="mergesort")
t = t[o]; d = d[o]; g = g[o]
uniq, first = np.unique(t, return_index=True)
nrisk = len(t) - first
dall = np.add.reduceat(d, first)
d1 = np.add.reduceat(d * g, first)
n1 = g.sum() - np.concatenate(([0], np.cumsum(g)))[first]
ok = dall > 0
nr, da, na = nrisk[ok], dall[ok], n1[ok]
e1 = float(np.sum(da * na / nr))
den = nr ** 2 * (nr - 1)
v = float(np.sum(np.where(nr > 1, da * (nr - da) * na * (nr - na) / np.where(den > 0, den, 1.0), 0.0)))
if v <= 0:
return None
chi2 = (float(d1.sum()) - e1) ** 2 / v
return chi2, float(stats.chi2.sf(chi2, 1))
def rmst(t, d, tau, wt=None):
"""Kaplan-Meier restricted mean survival time at tau with a Greenwood-type SE;
optional case weights give the population value (the SE is then not used)."""
o = np.argsort(t, kind="mergesort")
t = t[o]; d = d[o]
w = np.ones(len(t)) if wt is None else np.asarray(wt, float)[o]
uniq, first = np.unique(t, return_index=True)
nrisk = w.sum() - np.concatenate(([0.0], np.cumsum(w)))[first]
dj = np.add.reduceat(w * d, first)
keep = (uniq <= tau) & (dj > 0)
u, dj, nj = uniq[keep], dj[keep], nrisk[keep]
if u.size == 0:
return float(tau), 0.0
surv = np.concatenate(([1.0], np.cumprod(1.0 - dj / nj)))
strip = surv * np.diff(np.concatenate(([0.0], u, [tau])))
area = float(strip.sum())
a = area - np.concatenate(([0.0], np.cumsum(strip)))[1:u.size + 1]
den = nj * (nj - dj)
var = float(np.sum(np.where(den > 0, a ** 2 * dj / np.where(den > 0, den, 1.0), 0.0)))
return area, float(np.sqrt(max(var, 0.0)))
# ------------------------------------------------------------------ real data
df = load()
print("loaded", df.shape, list(df.columns))
print("dtypes:", {c: str(v) for c, v in df.dtypes.items()})
sub = df[(df["etype"] == 2) & (df["rx"].isin(["Obs", "Lev+5FU"]))].copy()
n_two_arm = int(len(sub))
n_missing = int(sub[["nodes", "differ"]].isna().any(axis=1).sum())
sub = sub.dropna(subset=["nodes", "differ"]).copy()
sub["lognodes"] = np.log(sub["nodes"].to_numpy(float) + 1.0)
TRT = (sub["rx"] == "Lev+5FU").to_numpy().astype(float)
TIME = sub["time"].to_numpy(float)
EVENT = sub["status"].to_numpy(int)
Z = sub[COVS].to_numpy(float)
NOBS = int(len(sub))
print(f"two-arm death records {n_two_arm}; dropped for missing nodes/differ {n_missing}; "
f"analysed {NOBS} (Lev+5FU {int(TRT.sum())}, Obs {int((1 - TRT).sum())}); "
f"deaths {int(EVENT.sum())}; censored {int((EVENT == 0).sum())}")
b_un, s_un = cox_fit(TRT[:, None], TIME, EVENT)
b_ad, s_ad = cox_fit(np.column_stack([TRT, Z]), TIME, EVENT)
sm_un = PHReg(TIME, TRT[:, None], status=EVENT, ties="breslow").fit()
sm_ad = PHReg(TIME, np.column_stack([TRT, Z]), status=EVENT, ties="breslow").fit()
val_cox = max(float(np.max(np.abs(b_un - sm_un.params))), float(np.max(np.abs(b_ad - sm_ad.params))),
float(np.max(np.abs(s_un - sm_un.bse))), float(np.max(np.abs(s_ad - sm_ad.bse))))
lr_full = logrank(TIME, EVENT, TRT.astype(int))
val_lr = float(abs(lr_full[0] - survdiff(TIME, EVENT, TRT)[0]))
print(f"validation vs statsmodels: Cox max abs diff {val_cox:.2e}; log-rank chi2 diff {val_lr:.2e}")
b_cov = cox_fit(Z, TIME, EVENT)[0]
PI = Z @ b_cov
STRAT = np.digitize(PI, np.quantile(PI, [1 / 3, 2 / 3]))
pi_gap = float(PI[TRT == 1].mean() - PI[TRT == 0].mean())
idx_a = [np.flatnonzero(TRT == 0), np.flatnonzero(TRT == 1)]
idx_as = [[np.flatnonzero((TRT == a) & (STRAT == k)) for k in range(3)] for a in (0, 1)]
prop = np.array([float((STRAT == k).mean()) for k in range(3)])
def alloc(n):
raw = prop * n
base = np.floor(raw).astype(int)
for j in np.argsort(-(raw - base))[: n - int(base.sum())]:
base[j] += 1
return base
def draw(dgm, n, rng):
if dgm == "D1":
i = np.concatenate([rng.choice(idx_a[a], n, True) for a in (0, 1)])
elif dgm == "D2":
k = alloc(n)
i = np.concatenate([rng.choice(idx_as[a][s], k[s], True) for a in (0, 1) for s in range(3)])
else:
i = rng.choice(NOBS, 2 * n, True)
return i, np.concatenate([np.zeros(n), np.ones(n)])
def weights(dgm):
"""Exact per-record probability mass of the resampling population."""
w = np.zeros(NOBS)
for a in (0, 1):
if dgm == "D1":
w[idx_a[a]] = 0.5 / idx_a[a].size
else:
for s in range(3):
w[idx_as[a][s]] = 0.5 * prop[s] / idx_as[a][s].size
return w
def reference(dgm):
if dgm == "D3":
return {m: 0.0 for m in METHODS}
w = weights(dgm)
bu = cox_fit(TRT[:, None], TIME, EVENT, wt=w)[0][0]
ba = cox_fit(np.column_stack([TRT, Z]), TIME, EVENT, wt=w)[0][0]
r = {}
for key, tau in (("M5", TAU1), ("M6", TAU2)):
m1 = TRT == 1; m0 = TRT == 0
r[key] = float(rmst(TIME[m1], EVENT[m1], tau, w[m1])[0] - rmst(TIME[m0], EVENT[m0], tau, w[m0])[0])
return {"M1": float(bu), "M2": float(bu), "M3": float(ba), "M4": float(ba), **r}
REF = {d: reference(d) for d in ("D1", "D2", "D3")}
print("exact reference values:", {k: {m: round(v, 4) for m, v in r.items()} for k, r in REF.items()})
def analyse(i, g):
t, e, z = TIME[i], EVENT[i], Z[i]
out = {}
lr = logrank(t, e, g.astype(int))
out["M1"] = None if lr is None else (np.nan, np.nan, lr[1], 0)
fu = cox_fit(g[:, None], t, e)
out["M2"] = None if fu is None else (fu[0][0], fu[1][0], 2 * stats.norm.sf(abs(fu[0][0] / fu[1][0])), 0)
fa = cox_fit(np.column_stack([g, z]), t, e)
out["M3"] = None if fa is None else (fa[0][0], fa[1][0], 2 * stats.norm.sf(abs(fa[0][0] / fa[1][0])), 0)
cur, keep, npass = fa, np.arange(len(COVS)), 0
if fa is not None:
b, s = fa
while True:
drop = 2 * stats.norm.sf(np.abs(b[1:] / s[1:])) > 0.10
if not drop.any() or npass >= 4:
break
keep = keep[~drop]; npass += 1
cur = cox_fit(np.column_stack([g, z[:, keep]]) if keep.size else g[:, None], t, e)
if cur is None:
break
b, s = cur
out["M4"] = None if cur is None else (
cur[0][0], cur[1][0], 2 * stats.norm.sf(abs(cur[0][0] / cur[1][0])), int(keep.size))
t1, e1, t0, e0 = t[g == 1], e[g == 1], t[g == 0], e[g == 0]
for key, tau in (("M5", TAU1), ("M6", TAU2)):
r1, v1 = rmst(t1, e1, tau); r0, v0 = rmst(t0, e0, tau)
se = float(np.hypot(v1, v0))
ex = int(t1.max() < tau) + int(t0.max() < tau)
out[key] = None if se <= 0 else (r1 - r0, se, 2 * stats.norm.sf(abs((r1 - r0) / se)), ex)
return out
# ----------------------------------------------------------------- the study
cells, pairs = {}, {}
for dgm in ("D1", "D2", "D3"):
for n in SIZES:
rng = np.random.default_rng(SEED + 1000 * ("D1", "D2", "D3").index(dgm) + n)
est = {m: [] for m in METHODS}; sev = {m: [] for m in METHODS}
cvr = {m: [] for m in METHODS}; rej = {m: np.zeros(NREP, bool) for m in METHODS}
fails = {m: 0 for m in METHODS}; nkeep = []; nex = 0
for b in range(NREP):
res = analyse(*draw(dgm, n, rng))
for m in METHODS:
r = res[m]
if r is None:
fails[m] += 1
continue
rej[m][b] = r[2] < ALPHA
if m == "M1":
continue
est[m].append(r[0]); sev[m].append(r[1])
cvr[m].append(abs(r[0] - REF[dgm][m]) <= ZCRIT * r[1])
if m == "M4":
nkeep.append(r[3])
if m == "M5":
nex += r[3]
row = {}
for m in METHODS:
p = float(rej[m].mean()); e = np.array(est[m], float)
row[m] = {"reject_pct": 100 * p, "reject_mcse": 100 * float(np.sqrt(p * (1 - p) / NREP)),
"fail_n": fails[m],
"mean_est": float(e.mean()) if e.size else None,
"bias": float(e.mean() - REF[dgm][m]) if e.size else None,
"emp_se": float(e.std(ddof=1)) if e.size else None,
"mod_se": float(np.mean(sev[m])) if sev[m] else None,
"rmse": float(np.sqrt(np.mean((e - REF[dgm][m]) ** 2))) if e.size else None,
"coverage_pct": 100 * float(np.mean(cvr[m])) if cvr[m] else None}
row["M4"]["mean_covariates_kept"] = float(np.mean(nkeep)) if nkeep else None
row["se_ratio_M3_over_M2"] = row["M3"]["mod_se"] / row["M2"]["mod_se"]
row["estimand_ratio_M3_over_M2"] = (row["M3"]["mean_est"] / row["M2"]["mean_est"]
if dgm != "D3" else None)
row["rmst_tau1_extrapolated_arms"] = int(nex)
cells[f"{dgm}_n{n}"] = row
for a, c in (("M3", "M1"), ("M4", "M1"), ("M2", "M1"), ("M5", "M1"), ("M6", "M1"), ("M3", "M2")):
x, y = rej[a], rej[c]
n01 = int(np.sum(~x & y)); n10 = int(np.sum(x & ~y))
se = float(np.sqrt(max(n01 + n10 - (n10 - n01) ** 2 / NREP, 0.0)) / NREP)
pairs[f"{dgm}_n{n}_{a}_minus_{c}"] = {"diff_pct": 100 * float(x.mean() - y.mean()),
"mcse_pct": 100 * se,
"discordant_a_only": n10, "discordant_c_only": n01}
print(f"{dgm} n={n}: " + " ".join(f"{m} {row[m]['reject_pct']:.2f}%" for m in METHODS))
d150 = pairs["D1_n150_M3_minus_M1"]; d300 = pairs["D1_n300_M3_minus_M1"]
t150 = cells["D3_n150"]["M3"]; t300 = cells["D3_n300"]["M3"]
supported = bool(d150["diff_pct"] > 2 * d150["mcse_pct"] and d300["diff_pct"] > 2 * d300["mcse_pct"]
and t150["reject_pct"] <= 5.0 + 2 * t150["reject_mcse"]
and t300["reject_pct"] <= 5.0 + 2 * t300["reject_mcse"])
out = {
"n_two_arm_death_records": n_two_arm, "n_dropped_missing_covariates": n_missing, "n_analysed": NOBS,
"n_treated": int(TRT.sum()), "n_control": int((1 - TRT).sum()), "n_deaths": int(EVENT.sum()),
"pct_censored": 100 * float((EVENT == 0).mean()), "median_followup_days": float(np.median(TIME)),
"max_followup_days": float(TIME.max()), "deaths_within_tau1": int(((TIME < TAU1) & (EVENT == 1)).sum()),
"at_risk_tau1_trt": int((TIME[TRT == 1] >= TAU1).sum()), "at_risk_tau1_ctl": int((TIME[TRT == 0] >= TAU1).sum()),
"at_risk_tau2_trt": int((TIME[TRT == 1] >= TAU2).sum()), "at_risk_tau2_ctl": int((TIME[TRT == 0] >= TAU2).sum()),
"tau1_days": float(TAU1), "tau2_days": float(TAU2), "n_replicates": NREP, "sizes_per_arm": list(SIZES),
"alpha": ALPHA, "seed": SEED, "validation_cox_max_abs_diff": val_cox, "validation_logrank_chi2_diff": val_lr,
"full_unadj_loghr": float(b_un[0]), "full_unadj_se": float(s_un[0]), "full_adj_loghr": float(b_ad[0]),
"full_adj_se": float(s_ad[0]), "full_unadj_hr": float(np.exp(b_un[0])), "full_adj_hr": float(np.exp(b_ad[0])),
"full_unadj_z": float(b_un[0] / s_un[0]), "full_adj_z": float(b_ad[0] / s_ad[0]),
"full_logrank_chi2": float(lr_full[0]), "full_logrank_p": float(lr_full[1]),
"prognostic_index_gap_trt_minus_ctl": pi_gap, "cov_beta": {c: float(v) for c, v in zip(COVS, b_cov)},
"reference_values": REF, "cells": cells, "paired_differences": pairs, "hypothesis_supported": supported,
}
print("RESULT_JSON: " + json.dumps(out))
4.Analysis
Analysis complete: experiment succeeded; hypothesis refuted; 16 key numbers.
Results and analysis
The experiment ran to completion on the real trial records (619 death records in the two randomised arms of interest, 25 dropped for missing nodes or differ, 594 analysed: 289 Lev+5FU and 305 Obs, 281 deaths, 52.7% censored, median follow-up 2003.5 days). The Newton-Raphson Breslow Cox fitter reproduced statsmodels PHReg to 8.7e-13 and the hand-coded log-rank statistic matched statsmodels survdiff to 2.1e-14, so the operating characteristics reported below are not artefacts of the estimator implementation. No replicate failed to converge in any of the 24,000 replicates (4,000 in each of six cells).
THE PRIMARY HYPOTHESIS IS REFUTED, on both of its clauses.
(1) Power. Under D1, the as-randomised bootstrap of the real trial, prespecified adjustment for the eight baseline covariates LOST power relative to the unadjusted log-rank test: 58.35% versus 63.43% at 150 per arm, a paired difference of -5.08 percentage points with a discordance-based Monte Carlo standard error of 0.62 (411 replicates where only the log-rank test rejected against 208 where only the adjusted Cox test did), and 86.75% versus 89.45% at 300 per arm, a difference of -2.70 points (MCSE 0.43). Both differences are more than four Monte Carlo standard errors on the wrong side of zero, so this is not noise.
(2) Type I error. Under D3, the permutation null built from the same records, the adjusted Cox Wald test rejected 6.35% (MCSE 0.39) of the time at 150 per arm and 6.45% (0.39) at 300 per arm, against a nominal 5%. The unadjusted analyses were correctly sized (log-rank 5.13% and 5.20%, unadjusted Cox 5.03% and 5.03%), which shows the resampling machinery itself is calibrated and locates the inflation in the adjusted model. The cause is visible in the same run: for the adjusted estimator the mean model-based standard error is 6.9% smaller than the empirical standard error under the null at 150 per arm (0.1724 against 0.1852), while for the unadjusted estimator the gap is only 1.9% (0.1684 against 0.1716). Confidence interval coverage tracks this, 93.65% for the adjusted and 94.98% for the unadjusted estimator.
WHY ADJUSTMENT LOST, AND WHEN IT WINS. The mechanism is exactly the two-ingredient decomposition the design was built to measure. Adjustment gains power only if it inflates the estimand by more than it inflates the standard error. In this trial the standard-error cost is real but modest, a ratio of 1.035 at 150 per arm and 1.024 at 300 per arm, and essentially the same under every data-generating mechanism. The estimand inflation, however, is absent: the exact reference values of the D1 population are a marginal log hazard ratio of -0.3854 and a conditional log hazard ratio of -0.3846, a ratio of 0.998, and the replicate-averaged ratio is 0.986 at 150 per arm and 0.992 at 300 per arm. The reason is a realised chance imbalance: the Lev+5FU arm has a mean baseline prognostic index 0.072 lower (better) than the observation arm, and correcting for that pushes the adjusted estimate back toward the null by almost exactly as much as non-collapsibility pushes it away.
D2 confirms this reading. When the resample is stratified on tertiles of the prognostic index so that the two arms carry identical risk distributions, the marginal estimand attenuates to -0.3243 while the conditional estimand stays at -0.3809, an estimand ratio of 1.175 against an unchanged standard-error ratio of 1.035, and adjustment now WINS by +9.55 points at 150 per arm (46.83% to 56.38%, MCSE 0.56) and +8.40 points at 300 per arm (79.18% to 87.58%, MCSE 0.48). The textbook result is therefore correct as a statement about populations and wrong as a guarantee about any particular trial.
SECONDARY FINDINGS. (a) Data-driven backward elimination at p > 0.10, retaining on average 3.03 of the eight covariates at 150 per arm and 3.87 at 300 per arm, did NOT inflate type I error beyond what full adjustment already costs (6.05% and 6.10% against 6.35% and 6.45%), so the expectation that selection would add inflation is itself refuted; under D1 it even recovered part of the lost power (60.13% against 58.35% at 150 per arm), by discarding the covariates that contribute variance without prognostic content. (b) The restricted mean survival time difference was much less powerful than the log-rank test on this trial's empirical distribution: -20.85 points at a 5-year restriction and -12.13 points at 6.5 years at 150 per arm under D1, and -18.70 and -8.80 points at 300 per arm, despite 251 of the 281 deaths occurring within five years. Its type I error was nominal throughout (4.80% to 5.30%). The gap halves as the restriction time is extended, so this is a cost of restriction rather than a defect of the estimand.
BIAS AND COVERAGE. All estimators were close to unbiased against the exact population reference values: the largest absolute bias in a log hazard ratio was 0.0068 (M4 under D2 at 150 per arm, 1.8% of the estimand) and the largest in an RMST difference was 2.2 days on a reference of 149.7. Coverage was between 93.1% and 96.9% in every cell, with the adjusted models consistently about a point low and the RMST estimators about a point high.
Key numbers
| Metric | Value | Interpretation |
|---|---|---|
| Analysed sample | 594 patients (289 Lev+5FU, 305 Obs), 281 deaths, 52.7% censored | Death records of the two arms after dropping 25 of 619 for missing nodes or differ; the sample every resampling population is built from. |
| Full-sample unadjusted log hazard ratio | -0.3855 (SE 0.1211), HR 0.680, z = -3.18, log-rank chi2 = 10.25, p = 0.0014 | The trial's own crude treatment effect, a 32% reduction in the hazard of death, consistent with the published Moertel result. |
| Full-sample adjusted log hazard ratio | -0.3852 (SE 0.1228), HR 0.680, z = -3.14 | Adjustment left the point estimate unchanged and raised the standard error, so the adjusted analysis was already weaker in the trial itself. |
| Baseline prognostic index gap (Lev+5FU minus Obs) | -0.072 on the log-hazard scale | The realised chance imbalance: the treated arm was slightly healthier at baseline, which is what cancels the non-collapsibility inflation. |
| Estimand ratio, adjusted over unadjusted (exact reference values) | D1: -0.3846 / -0.3854 = 0.998; D2: -0.3809 / -0.3243 = 1.175 | Non-collapsibility inflation is absent in the as-randomised population and worth 17.5% once covariate balance is enforced. |
| Standard-error ratio, adjusted over unadjusted | 1.035 at n = 150 per arm and 1.024 at n = 300 per arm, essentially identical under all three mechanisms | The price of adjustment: about 2 to 3.5% more standard error, paid whether or not the estimand inflates. |
| Primary paired power difference, adjusted Cox minus log-rank, D1 | -5.08 points (MCSE 0.62) at n = 150; -2.70 points (MCSE 0.43) at n = 300 | The refutation: in the trial's own resampling world adjustment costs power at both sizes, by more than four Monte Carlo standard errors. |
| Power under D1 | n = 150: log-rank 63.43%, unadjusted Cox 62.88%, adjusted Cox 58.35%, selected Cox 60.13%; n = 300: 89.45%, 89.35%, 86.75%, 86.80% | The realistic scenario, preserving the trial's actual covariate imbalance. |
| Power under D2 (risk-balanced resampling) | n = 150: log-rank 46.83% vs adjusted Cox 56.38% (+9.55, MCSE 0.56); n = 300: 79.18% vs 87.58% (+8.40, MCSE 0.48) | With chance imbalance removed the textbook gain reappears and is large, confirming the mechanism rather than contradicting the theory. |
| Type I error under the permutation null D3 | log-rank 5.13% / 5.20%; unadjusted Cox 5.03% / 5.03%; adjusted Cox 6.35% / 6.45%; selected Cox 6.05% / 6.10% (MCSE about 0.35-0.39) | The unadjusted analyses are correctly sized; the adjusted Wald test is anti-conservative by about 1.3 to 1.5 points, and data-driven selection adds nothing on top of that. |
| Model-based versus empirical standard error under the null, n = 150 | adjusted 0.1724 vs 0.1852 (6.9% too small); unadjusted 0.1684 vs 0.1716 (1.9% too small) | The direct explanation of the adjusted test's size inflation and its 93.65% coverage. |
| Covariates retained by backward elimination at p > 0.10 | 3.03 of 8 on average at n = 150, 3.87 at n = 300 | Selection discards most of the prespecified set, which is why it recovers some power without adding size distortion. |
| RMST difference versus log-rank, D1 | tau = 5 years: -20.85 points at n = 150 and -18.70 at n = 300; tau = 6.5 years: -12.13 and -8.80 | Restricting the mean survival time costs a great deal of power on this trial's distribution, and about half the loss is recovered by extending the restriction time. |
| Reference RMST differences | D1: 119.1 days at 5 years, 187.6 days at 6.5 years; D2: 91.6 and 149.7 days | The exact population values used for bias and coverage; the D2 attenuation mirrors the attenuation of the marginal hazard ratio. |
| Monte Carlo design | 4,000 replicates per cell, six cells, 24,000 replicates, no convergence failures; MCSE on a rejection rate at most 0.79 points | Enough replicates that every difference discussed exceeds three Monte Carlo standard errors. |
| Implementation validation | Cox coefficients and standard errors within 8.7e-13 of statsmodels PHReg; log-rank chi2 within 2.1e-14 of statsmodels survdiff | The hand-written estimators used for speed are numerically identical to the reference implementations. |
Caveats
- This is one trial, one endpoint and one prespecified covariate set. The direction of the D1 result depends on the sign and size of the realised baseline imbalance in this particular randomisation, which is a fixed feature of the dataset and cannot be replicated across trials from these data alone.
- The bootstrap resamples with replacement from 594 records, so replicates contain duplicated patients and therefore more tied event times than a fresh sample of the same size would. All Cox fits use the Breslow approximation for ties; Efron's approximation was not compared, and the study cannot rule out a small tie-related contribution to the adjusted Wald test's size inflation.
- The D2 balancing removes only first-order imbalance, in tertiles of a one-dimensional prognostic index estimated from the same 594 records. Residual within-stratum imbalance and imbalance in directions orthogonal to the index remain, and the index is data-dependent, so D2 is an idealisation rather than a re-randomisation.
- Twenty-five of 619 patients (4.0%) were dropped for missing nodes or differ. No imputation was attempted, so the resampling population is the complete-case population.
- Because the data-driven model's estimand changes from replicate to replicate, the bias and coverage reported for the selected Cox model are measured against the full adjusted conditional estimand and should be read as approximate.
- Only Wald tests were used for the Cox models. The score test (of which the log-rank test is the unadjusted case) and the likelihood ratio test are known to behave differently in small samples and might not show the same size inflation.
- The treatment effect is fixed at the magnitude actually observed in this trial (hazard ratio 0.68); the study does not sweep effect sizes, event rates or censoring patterns, so the numerical size of the power differences should not be extrapolated to trials with different event rates.
- The restricted mean survival time conclusions depend on the two restriction times chosen (5 and 6.5 years); at 6.5 years only 85 and 64 patients remain at risk in the full sample, so the longer restriction is itself near the edge of the observed follow-up.
5.Paper writing
Manuscript drafted: “Covariate adjustment does not automatically buy power in a randomised survival trial: a plasmode resampling study of the NCCTG adjuvant colon cancer trial” (≈ 5277 words).
≈ 5277 words · AI-drafted manuscript · not peer-reviewed
Covariate adjustment does not automatically buy power in a randomised survival trial: a plasmode resampling study of the NCCTG adjuvant colon cancer trial
Abstract
Prespecified adjustment for prognostic baseline covariates is routinely recommended to improve the efficiency of randomised trials. For the Cox model the argument is indirect: adjustment inflates the standard error of the treatment coefficient, but non-collapsibility inflates the conditional estimand by more, so the test is expected to gain power. That reasoning has been validated almost entirely by parametric simulation, in which covariates are balanced in expectation by construction. We tested it in a real trial's own resampling world. Using the 594 complete-case death records of the observation and levamisole-plus-fluorouracil arms of the NCCTG/Mayo adjuvant colon cancer trial, we built three plasmode data-generating mechanisms by bootstrapping the observed records: as-randomised, risk-balanced, and a permutation null. Over 4,000 replicates per cell at 150 and 300 patients per arm we compared the log-rank test, unadjusted Cox, Cox adjusted for eight prespecified covariates, Cox with within-replicate backward elimination, and restricted mean survival time (RMST) differences at 5 and 6.5 years. Adjustment lost power under the as-randomised mechanism, by 5.08 percentage points (Monte Carlo standard error, MCSE, 0.62) at 150 per arm and 2.70 points (0.43) at 300, because the trial's realised covariate imbalance cancelled the non-collapsibility inflation (estimand ratio 0.998) while the standard-error cost (ratio 1.035) remained. Resampling with baseline risk balanced restored the textbook gain (+9.55 points, MCSE 0.56). The adjusted Wald test was also anti-conservative (6.35% and 6.45% at alpha = 0.05). Trialists should treat the efficiency gain from Cox adjustment as a population property, not a guarantee for their trial.
Keywords: covariate adjustment; Cox proportional hazards model; non-collapsibility; plasmode simulation; restricted mean survival time; statistical power
1. Introduction
Randomisation makes a crude treatment comparison unbiased, but it does not make it efficient. Adjusting the primary analysis for strongly prognostic baseline covariates is therefore standard advice, and the empirical case for it in real trials is documented [1]. For linear models the case is simple: the estimand is unchanged and the residual variance falls. For non-linear models it is not. Robinson and Jewell showed for logistic regression that adjustment increases the standard error of the log odds ratio, yet increases the magnitude of the estimand by more, so that the test still gains [2]. Ford, Norrie and Ahmadi gave the survival analogue under the heading of model inconsistency: if conditional hazards are proportional, marginal hazards generally are not, so the adjusted and unadjusted Cox models cannot both be correctly specified and the marginal log hazard ratio is attenuated toward the null relative to the conditional one [3]. The unadjusted fit remains interpretable as a consistent estimator of a well-defined limiting parameter under this misspecification [4].
The resulting power claim has been quantified almost entirely by parametric Monte Carlo simulation. Hernández, Eijkemans and Steyerberg asked directly how much prespecified adjustment increases power for time-to-event outcomes [5], and Momal et al. report sample-size reductions from 3.1% to 85.7% depending on prognostic strength and cumulative incidence [6]. In every such design the covariates are balanced across arms in expectation by construction, and the realised imbalance in a replicate is a fresh random draw.
A real trial is not like that: its realised imbalance is a fixed feature of the dataset. Adjustment then does two things at once, inflating the estimand through non-collapsibility and correcting the crude estimate for that particular imbalance. The two can cancel. Whether the promised gain survives in a real trial is therefore an empirical question a parametric simulation cannot answer.
This paper answers it for one trial and explains the answer rather than merely reporting it. Our contributions are: (i) a plasmode design whose data-generating mechanism is a real randomised trial, with every estimand's reference value computed exactly inside the resampling population; (ii) a decomposition of the adjusted-versus-unadjusted power difference into an estimand ratio and a standard-error ratio, whose relative size fixes the sign of the difference; (iii) a paired evaluation, with discordance-based Monte Carlo standard errors, of five analysis strategies including within-replicate covariate selection and RMST contrasts.
2. Methods
Let denote the survival time of patient , the censoring time, , , the randomised arm and a vector of baseline covariates. Two Cox models are considered,
whose treatment coefficients are the marginal and conditional log hazard ratios. Both are estimated by maximising the Breslow partial likelihood, with solving where , and model-based standard errors taken from the inverse observed information. Under non-collapsibility when is prognostic and balanced [3]; under misspecification is still consistent for a well-defined functional [4].
Write for the estimand ratio and for the standard-error ratio. Since the Wald statistic is , adjustment increases power if and only if . Both quantities are estimable by resampling; the study measures them directly.
Six analyses were applied to every replicate: M1, the log-rank test with the hypergeometric variance; M2, the Wald test of ; M3, the Wald test of with all eight prespecified covariates; M4, the same after within-replicate backward elimination, dropping at each pass every covariate with Wald and refitting (treatment always retained, at most four passes); and M5, M6, the difference in restricted mean survival time from arm-specific Kaplan-Meier curves with the Greenwood-type variance , at and years [7]. All tests are two-sided at ; all intervals are 95% Wald intervals. The Cox and log-rank routines were written in NumPy for speed and validated against statsmodels: coefficients and standard errors agreed with PHReg (Breslow ties) to , and the log-rank statistic with survdiff to .
3. Simulation Study
3.1 Aims
To determine whether prespecified Cox adjustment increases power over the unadjusted log-rank test in the resampling population of a real randomised trial; to decompose any difference into and ; and to assess type I error, bias and coverage for the same strategies, within-replicate covariate selection and RMST contrasts.
3.2 Data-generating mechanisms
The data-generating mechanism is a real trial, not a parametric generator. We used the colon dataset of the R survival package, mirrored by the Rdatasets archive (https://vincentarelbundock.github.io/Rdatasets/csv/survival/colon.csv, retrieved 2026-09-08T15:47:32Z, sha256 6f3472a6...), which holds the patient records of the NCCTG/Mayo adjuvant chemotherapy trial in stage B/C colon cancer reported by Moertel et al. [8]. The file has 1,858 rows in long form, two per patient (etype = 1 recurrence, etype = 2 death), so 929 patients are represented. time is days from randomisation to event or censoring and status the 0/1 indicator; rx is the randomised arm (Obs, Lev, Lev+5FU); age is in years, nodes is the number of positive lymph nodes, differ tumour differentiation (1 well to 3 poor), extent depth of invasion (1 submucosa to 4 contiguous structures), and sex, obstruct, perfor, adhere, surg, node4 are 0/1 clinical indicators.
Filters, applied in this order: death records only (etype = 2), arms Obs and Lev+5FU only (619 patients), and complete cases on nodes and differ (25 patients dropped, 4.0%). The analysed set is 594 patients, 289 on Lev+5FU and 305 on observation, with 281 deaths (52.7% censored), median observed time to death or censoring 2,003.5 days and maximum 3,309 days (9.1 years). The prespecified covariate vector is age, sex, obstruct, adhere, surg, , differ, extent; perfor was excluded a priori as too rare for stable estimation, and node4 is a dichotomisation of nodes and so redundant.
Three mechanisms resample these 594 records with master seed 20260908 (Table 1). D1 (as-randomised) draws patients with replacement within each arm, preserving the real treatment effect, covariate joint distribution, censoring pattern and realised imbalance. D2 (risk-balanced) does the same but stratified on tertiles of the baseline prognostic index from a covariate-only Cox fit, allocating both arms to the pooled tertile proportions. D3 (permutation null) draws patients with replacement from the pooled sample and splits them at random into two arms of , giving an exactly null treatment effect with the real covariate-outcome relationship intact. Per-arm sizes were and ; the latter reproduces the size of the trial itself.
D2 needs a caution that governs how the whole D1-to-D2 contrast should be read. Forcing both arms to the pooled tertile proportions changes each arm's covariate distribution, not merely the difference between the two, so D2 is a different resampling population from D1 and not D1 with chance imbalance switched off. The shift in the marginal estimand from under D1 to under D2 (Table 1) is a consequence of that change of population. Balancing is also only first-order: the index is one-dimensional and estimated from the same 594 records, so residual within-stratum imbalance, and imbalance in directions orthogonal to the index, survive.
Following the caution of Shaw et al. that a plasmode reference value must belong to the resampling population [9], each estimand's reference value was computed exactly, by fitting the same estimator to the 594 records with case weights equal to the per-record probability mass of that population, rather than by a large draw.
Table 1. The analysed trial sample, the three resampling populations and their exact estimand reference values.
| Item | D1 as-randomised | D2 risk-balanced | D3 permutation null |
|---|---|---|---|
| Records resampled | 594 (289 / 305) | 594, stratified on prognostic-index tertiles | 594 pooled |
| Treatment effect present | yes | yes | no (exactly null) |
| Marginal log HR | -0.3854 | -0.3243 | 0 |
| Conditional log HR | -0.3846 | -0.3809 | 0 |
| Estimand ratio | 0.998 | 1.175 | – |
| RMST difference, = 5 years (days) | 119.1 | 91.6 | 0 |
| RMST difference, = 6.5 years (days) | 187.6 | 149.7 | 0 |
Notes. Per-arm sizes = 150 and 300 in every mechanism; 4,000 replicates per cell, 24,000 in total, master seed 20260908. Reference values are exact population parameters, obtained by weighted Breslow Cox and weighted Kaplan-Meier fits to the 594 records, so they carry no Monte Carlo error. D1 and D2 are different populations, so their reference values are not directly comparable. The prognostic index uses the covariate-only Cox coefficients age 0.0023, sex -0.0991, obstruct 0.1094, adhere 0.2564, surg 0.2929, log(nodes+1) 0.7805, differ 0.1195, extent 0.3591; its mean is 0.072 lower (better) in the Lev+5FU arm, which is the realised chance imbalance. In the full sample 251 of the 281 deaths occur within 5 years; 178 and 153 patients are at risk at 5 years, 85 and 64 at 6.5 years.
3.3 Estimands
The marginal log hazard ratio (M1, M2), the conditional log hazard ratio (M3, approximately M4), and the RMST difference at and (M5, M6). These are different quantities: rejection rates are comparable across them, estimation performance is not.
3.4 Performance measures
Empirical rejection rate at (power under D1 and D2, type I error under D3); bias, empirical standard error, RMSE and 95% interval coverage against the exact reference; mean model-based standard error; mean number of covariates retained by M4; and non-convergence counts. Following Morris, White and Crowther [10], all methods were applied to the same replicate, so head-to-head differences are reported with a discordance-based Monte Carlo standard error, , where and count replicates in which only one method rejects. With the MCSE of a rejection rate is at most 0.79 points and that of a paired difference typically 0.4 to 0.8, which resolves differences of 2 points. No replicate failed to converge.
3.5 Results
Table 2. Empirical rejection rate (%) at = 0.05, with Monte Carlo standard errors, from 4,000 replicates per cell.
| Mechanism | /arm | M1 log-rank | M2 Cox unadj. | M3 Cox adj. | M4 Cox selected | M5 RMST 5y | M6 RMST 6.5y |
|---|---|---|---|---|---|---|---|
| D1 | 150 | 63.43 (0.76) | 62.88 (0.76) | 58.35 (0.78) | 60.13 (0.77) | 42.58 (0.78) | 51.30 (0.79) |
| D1 | 300 | 89.45 (0.49) | 89.35 (0.49) | 86.75 (0.54) | 86.80 (0.54) | 70.75 (0.72) | 80.65 (0.62) |
| D2 | 150 | 46.83 (0.79) | 46.35 (0.79) | 56.38 (0.78) | 56.90 (0.78) | 25.73 (0.69) | 34.20 (0.75) |
| D2 | 300 | 79.18 (0.64) | 78.95 (0.64) | 87.58 (0.52) | 87.10 (0.53) | 47.70 (0.79) | 62.33 (0.77) |
| D3 | 150 | 5.13 (0.35) | 5.03 (0.35) | 6.35 (0.39) | 6.05 (0.38) | 4.98 (0.34) | 4.80 (0.34) |
| D3 | 300 | 5.20 (0.35) | 5.03 (0.35) | 6.45 (0.39) | 6.10 (0.38) | 4.93 (0.34) | 5.30 (0.35) |
Notes. D1 and D2 rows are power, D3 rows type I error. Rates are not comparable between D1 and D2, which are different populations. M4 retained on average 3.03 of 8 covariates at = 150 and 3.87 at = 300 under D1.
Table 3. Paired difference in rejection rate against the log-rank test (percentage points), with discordance-based Monte Carlo standard errors.
| Mechanism | /arm | M2 − M1 | M3 − M1 | M4 − M1 | M5 − M1 | M6 − M1 |
|---|---|---|---|---|---|---|
| D1 | 150 | -0.55 (0.12) | -5.08 (0.62) | -3.30 (0.61) | -20.85 (0.70) | -12.13 (0.58) |
| D1 | 300 | -0.10 (0.05) | -2.70 (0.43) | -2.65 (0.43) | -18.70 (0.63) | -8.80 (0.47) |
| D2 | 150 | -0.48 (0.11) | +9.55 (0.56) | +10.08 (0.55) | -21.10 (0.70) | -12.63 (0.58) |
| D2 | 300 | -0.23 (0.07) | +8.40 (0.48) | +7.93 (0.47) | -31.48 (0.75) | -16.85 (0.61) |
| D3 | 150 | -0.10 (0.05) | +1.23 (0.34) | +0.93 (0.34) | -0.15 (0.33) | -0.33 (0.28) |
| D3 | 300 | -0.18 (0.07) | +1.25 (0.36) | +0.90 (0.35) | -0.28 (0.33) | +0.10 (0.29) |
Notes. Bold entries are the study's primary comparison. Negative values favour the log-rank test. Differences are within-mechanism and paired on the replicate. Under D1 at = 150 there were 411 replicates in which only the log-rank test rejected against 208 in which only the adjusted Cox test did.
The primary hypothesis is refuted on both clauses. Under D1 the adjusted Cox test lost 5.08 points of power at 150 per arm and 2.70 at 300, each more than four MCSEs on the wrong side of zero. Under D3 it rejected 6.35% and 6.45% of the time against a nominal 5%, while the log-rank and unadjusted Cox tests were correctly sized (5.03% to 5.20%): the resampling machinery is calibrated, and the inflation sits in the adjusted model.
Table 4 records the mechanism of that inflation, though not its cause. Under D3 the adjusted estimator's mean model-based standard error is 6.9% below its empirical standard error (0.1724 against 0.1852 at = 150), against 1.9% for the unadjusted estimator, and coverage follows at 93.65% versus 94.98%. That discrepancy is the inflation restated rather than an explanation of it, and this study does not separate the two candidate causes: finite-sample bias of the observed information when eight nuisance parameters are estimated alongside the treatment effect, and the tie burden created by drawing 150 or 300 patients with replacement from 594 records, handled throughout by the Breslow approximation. All estimators are close to unbiased against their exact reference values (Tables 4 and 5); the largest absolute bias in a log hazard ratio anywhere in the study is 0.0068, for M4 under D2 at = 150, which is 1.8% of the estimand.
Table 4. Estimation performance for the treatment log hazard ratio: unadjusted (M2), adjusted (M3) and selected (M4) Cox, 4,000 replicates per cell.
| Mechanism | /arm | Method | Reference | Mean | Bias | Empirical SE | Model SE | RMSE | Coverage (%) |
|---|---|---|---|---|---|---|---|---|---|
| D1 | 150 | M2 | -0.3854 | -0.3888 | -0.0034 | 0.1745 | 0.1708 | 0.1745 | 93.75 |
| D1 | 150 | M3 | -0.3846 | -0.3833 | +0.0013 | 0.1874 | 0.1767 | 0.1874 | 93.38 |
| D1 | 150 | M4 | -0.3846 | -0.3823 | +0.0022 | 0.1859 | 0.1731 | 0.1859 | 93.13 |
| D1 | 300 | M2 | -0.3854 | -0.3880 | -0.0026 | 0.1237 | 0.1206 | 0.1237 | 94.40 |
| D1 | 300 | M3 | -0.3846 | -0.3849 | -0.0003 | 0.1303 | 0.1234 | 0.1303 | 93.55 |
| D1 | 300 | M4 | -0.3846 | -0.3829 | +0.0016 | 0.1303 | 0.1219 | 0.1303 | 93.48 |
| D2 | 150 | M2 | -0.3243 | -0.3223 | +0.0020 | 0.1619 | 0.1704 | 0.1619 | 96.03 |
| D2 | 150 | M3 | -0.3809 | -0.3772 | +0.0037 | 0.1857 | 0.1764 | 0.1857 | 93.33 |
| D2 | 150 | M4 | -0.3809 | -0.3741 | +0.0068 | 0.1845 | 0.1729 | 0.1846 | 93.10 |
| D2 | 300 | M2 | -0.3243 | -0.3260 | -0.0017 | 0.1126 | 0.1203 | 0.1126 | 95.93 |
| D2 | 300 | M3 | -0.3809 | -0.3820 | -0.0010 | 0.1255 | 0.1232 | 0.1255 | 94.33 |
| D2 | 300 | M4 | -0.3809 | -0.3781 | +0.0028 | 0.1257 | 0.1219 | 0.1257 | 94.10 |
| D3 | 150 | M2 | 0 | +0.0003 | +0.0003 | 0.1716 | 0.1684 | 0.1716 | 94.98 |
| D3 | 150 | M3 | 0 | -0.0009 | -0.0009 | 0.1852 | 0.1724 | 0.1852 | 93.65 |
| D3 | 150 | M4 | 0 | -0.0011 | -0.0011 | 0.1827 | 0.1701 | 0.1827 | 93.95 |
| D3 | 300 | M2 | 0 | -0.0013 | -0.0013 | 0.1180 | 0.1189 | 0.1180 | 94.98 |
| D3 | 300 | M3 | 0 | -0.0010 | -0.0010 | 0.1254 | 0.1204 | 0.1253 | 93.55 |
| D3 | 300 | M4 | 0 | -0.0010 | -0.0010 | 0.1243 | 0.1197 | 0.1243 | 93.90 |
Notes. Coverage is of the exact reference value by the 95% Wald interval; its MCSE is at most 0.36 points. M4's estimand changes from replicate to replicate, so its bias and coverage are measured against the full adjusted conditional estimand and are approximate. Under D2 the unadjusted estimator's empirical SE falls below its model SE, hence coverage above 95%: the stratified resample removes between-arm variation in baseline risk that the unstratified variance formula still charges for.
Table 5. Estimation performance for the restricted mean survival time difference in days, = 5 years (M5) and 6.5 years (M6), 4,000 replicates per cell.
| Mechanism | /arm | Method | Reference | Mean | Bias | Empirical SE | Model SE | RMSE | Coverage (%) |
|---|---|---|---|---|---|---|---|---|---|
| D1 | 150 | M5 | 119.11 | 119.56 | +0.45 | 68.33 | 66.98 | 68.32 | 94.35 |
| D1 | 150 | M6 | 187.55 | 188.30 | +0.75 | 95.96 | 93.96 | 95.95 | 94.23 |
| D1 | 300 | M5 | 119.11 | 120.16 | +1.05 | 48.52 | 47.43 | 48.52 | 94.60 |
| D1 | 300 | M6 | 187.55 | 188.90 | +1.35 | 68.08 | 66.53 | 68.08 | 94.62 |
| D2 | 150 | M5 | 91.64 | 90.01 | -1.64 | 64.15 | 67.18 | 64.16 | 96.12 |
| D2 | 150 | M6 | 149.65 | 147.45 | -2.20 | 89.61 | 94.20 | 89.63 | 95.93 |
| D2 | 300 | M5 | 91.64 | 91.60 | -0.04 | 43.29 | 47.55 | 43.28 | 96.88 |
| D2 | 300 | M6 | 149.65 | 149.89 | +0.24 | 60.85 | 66.68 | 60.84 | 96.92 |
| D3 | 150 | M5 | 0 | -0.40 | -0.40 | 67.97 | 67.32 | 67.96 | 95.03 |
| D3 | 150 | M6 | 0 | -0.40 | -0.40 | 95.81 | 94.56 | 95.80 | 95.20 |
| D3 | 300 | M5 | 0 | +0.32 | +0.32 | 47.71 | 47.72 | 47.70 | 95.08 |
| D3 | 300 | M6 | 0 | +0.50 | +0.50 | 66.92 | 67.01 | 66.91 | 94.70 |
Notes. All quantities are in days. The largest absolute bias is 2.20 days on a reference of 149.65. At = 5 years no replicate had either arm's follow-up end before the restriction time, so no Kaplan-Meier curve required extrapolation; the equivalent count was not recorded at 6.5 years. The over-coverage under D2 has the same origin as in Table 4.
Table 6. The two-ingredient decomposition: adjustment gains power if and only if the estimand ratio exceeds the standard-error ratio.
| Mechanism | /arm | Estimand ratio | SE ratio | Observed M3 − M2 (points) | |
|---|---|---|---|---|---|
| D1 | 150 | 0.986 | 1.035 | 0.953 | -4.53 (0.62) |
| D1 | 300 | 0.992 | 1.024 | 0.969 | -2.60 (0.43) |
| D2 | 150 | 1.170 | 1.035 | 1.131 | +10.03 (0.56) |
| D2 | 300 | 1.172 | 1.025 | 1.143 | +8.63 (0.49) |
| D3 | 150 | – | 1.024 | – | +1.33 (0.34) |
| D3 | 300 | – | 1.012 | – | +1.43 (0.36) |
Notes. is the ratio of replicate-averaged adjusted to unadjusted estimates, the ratio of their mean model-based standard errors; the exact population is 0.998 (D1) and 1.175 (D2), and is undefined under the null. The sign of matches the sign of the observed difference in all four alternative cells; the apparent D3 "gain" of 1.3 points is size inflation, not power. Because D2 is a different population from D1 rather than D1 with imbalance removed, the rise in between the two rows should be read as the estimand ratio of a balanced population, not as a within-population effect of balancing.
Table 6 carries the mechanism. The standard-error cost of adjustment is real but small and nearly constant, = 1.02 to 1.04 in every cell. The estimand ratio is what moves: in the trial as randomised there is no inflation at all ( = 0.998 exactly), because the Lev+5FU arm happened to receive patients with a mean prognostic index 0.072 lower than the observation arm, and correcting for that advantage pushes the adjusted estimate back toward the null by almost exactly as much as non-collapsibility pushes it away. In the balanced population D2, is 1.175 and adjustment wins by 8 to 10 points. The theory of Ford et al. [3] is thus vindicated as a statement about populations and refuted as a guarantee about any one trial.
Two secondary findings. First, within-replicate backward elimination, retaining about 3 to 4 of the 8 covariates, did not inflate type I error beyond what full adjustment already costs (6.05% and 6.10% against 6.35% and 6.45%), and under D1 it recovered part of the lost power (60.13% against 58.35% at = 150) by discarding covariates that add variance without prognostic content; the common expectation that data-driven selection inflates size was not borne out here. Second, RMST contrasts were markedly less powerful than the log-rank test on this trial's empirical distribution, by 20.85 points at = 5 years and 12.13 at = 6.5 years ( = 150, D1), even though 251 of the 281 deaths occur within five years; their type I error was nominal (4.80% to 5.30%). Because the deficit roughly halves as is extended, this is a cost of restriction rather than a defect of the estimand, consistent with Tian et al. [7] that the log-rank test is more efficient when hazards are approximately proportional.
4. Illustrative Example: the trial itself
This section is not an independent application: it analyses the same 594 patients that define the resampling populations, so it is the finite-sample realisation of D1 rather than an out-of-sample illustration. Its purpose is to show that the mechanism is visible in the single conventional analysis a trialist would report. Table 7 gives that analysis: crude and adjusted hazard ratios are indistinguishable (0.680 both times) while the adjusted standard error is 1.4% larger, so the adjusted analysis is the weaker of the two, exactly as the resampling study predicts for this population.
Table 7. Conventional analysis of the 594 analysed patients of the NCCTG adjuvant colon cancer trial (real data, overall survival).
| Analysis | log HR | SE | HR (95% CI) | Wald |
|---|---|---|---|---|
| Log-rank test | – | – | – | = 10.25, = 0.0014 |
| Unadjusted Cox | -0.3855 | 0.1211 | 0.680 (0.536, 0.862) | -3.18 |
| Cox adjusted for 8 covariates | -0.3852 | 0.1228 | 0.680 (0.535, 0.865) | -3.14 |
Notes. Lev+5FU versus observation; 289 versus 305 patients, 281 deaths, 52.7% censored, median observed time to death or censoring 2,003.5 days. Adjustment covariates as in Section 3.2. Confidence intervals are Wald intervals on the log scale. The estimated hazard reduction of 32% is the trial's own overall-survival result [8].
5. Discussion
Findings. In the resampling world of a real randomised trial, prespecified Cox adjustment for a strongly prognostic covariate set reduced power by 5.08 points at 150 patients per arm and 2.70 points at 300, and produced a Wald test that rejected 6.4% of the time under a genuine null. In a balanced resampling population built from the same records, the same adjustment gained 8 to 10 points. Both results follow from one inequality, : the price of adjustment is a 2 to 4% larger standard error, and whether it is worth paying depends on whether non-collapsibility inflation survives the trial's realised imbalance.
Recommendations. Three follow from the scenarios studied. (i) The efficiency gain from Cox adjustment should be presented as an expectation over randomisations, not a property of a given trial: where the realised imbalance favours the experimental arm, adjustment can cost power, as it did here. (ii) The size inflation we measured for the adjusted Cox Wald test, 6.4% against a nominal 5%, is a caution about this design and not an established property of the test in a real trial of 150 to 300 patients per arm. It was observed under a with-replacement bootstrap whose replicates duplicate patients and therefore carry heavier ties than a fresh sample of the same size; all fits used the Breslow approximation, and Efron's approximation, the score test and the likelihood ratio test were not evaluated. It is a reason to check the size of an adjusted Wald test in one's own setting, not a general finding. (iii) Under approximately proportional hazards, an RMST contrast should be expected to be substantially less powerful than the log-rank test unless is set close to the end of follow-up.
Limitations. This is one trial, one endpoint and one covariate set; the sign of the D1 result depends on the realised imbalance of this particular randomisation, which these data cannot replicate. The bootstrap resamples with replacement, so replicates contain duplicated patients and more tied event times than a fresh sample would, and the resulting tie burden is confounded with every small-sample effect reported here. D2 removes only first-order imbalance, in tertiles of a one-dimensional index estimated from the same records, and changes the resampling population as Section 3.2 describes; it is an idealisation, not a re-randomisation. Twenty-five of 619 patients (4.0%) were dropped for missing nodes or differ with no imputation, so the resampling population is the complete-case population. Because the selected model's estimand varies across replicates, the bias and coverage of M4 are approximate. The treatment effect is fixed at the observed magnitude (HR 0.68) and the covariate set at eight variables, so the study yields single points rather than a curve, and the numerical size of the differences should not be extrapolated to trials with different effect sizes, event rates or prognostic strength. The RMST conclusions depend on the two restriction times chosen; at 6.5 years only 85 and 64 patients remain at risk in the full sample.
Extensions. Natural next steps are to repeat the design across a library of real trials, so that the distribution of over randomisations is characterised rather than a single draw from it; to sweep effect size and prognostic strength within one trial's records; and to compare Efron's tie handling and robust or small-sample variance estimators for the adjusted Wald test, which would settle the cause of the size inflation left open here.
Data Availability and Software
The data are public. The colon dataset of the R survival package was retrieved from the Rdatasets archive at https://vincentarelbundock.github.io/Rdatasets/csv/survival/colon.csv on 2026-09-08 (sha256 6f3472a6..., 1,858 rows, 84,108 bytes), redistributed under LGPL (>= 2) with the package's COPYRIGHTS file recording "Copyright 2000 Mayo Foundation for Medical Education and Research"; the underlying trial is reported by Moertel et al. [8]. The analysis is one self-contained Python 3 script using NumPy, SciPy and statsmodels, with master seed 20260908; it loads the real file, applies the Section 3.2 filters and runs 24,000 replicates in under a minute. No data were simulated: every replicate is a bootstrap draw from the observed patient records.
References
- Kahan BC, Jairath V, Doré CJ, Morris TP. The risks and rewards of covariate adjustment in randomized trials: an assessment of 12 outcomes from 8 studies. Trials. 2014;15:139. doi:10.1186/1745-6215-15-139
- Robinson LD, Jewell NP. Some surprising results about covariate adjustment in logistic regression models. Int Stat Rev. 1991;59(2):227-240. doi:10.2307/1403444
- Ford I, Norrie J, Ahmadi S. Model inconsistency, illustrated by the Cox proportional hazards model. Stat Med. 1995;14(8):735-746. doi:10.1002/sim.4780140804
- Lin DY, Wei LJ. The robust inference for the Cox proportional hazards model. J Am Stat Assoc. 1989;84(408):1074-1078. doi:10.1080/01621459.1989.10478874
- Hernández AV, Eijkemans MJC, Steyerberg EW. Randomized controlled trials with time-to-event outcomes: how much does prespecified covariate adjustment increase power? Ann Epidemiol. 2006;16(1):41-48. doi:10.1016/j.annepidem.2005.09.007
- Momal R, Li H, Trichelair P, Blum MGB, Balazard F. More efficient and inclusive time-to-event trials with covariate adjustment: a simulation study. Trials. 2023;24:380. doi:10.1186/s13063-023-07375-0
- Tian L, Fu H, Ruberg SJ, Uno H, Wei LJ. Efficiency of two sample tests via the restricted mean survival time for analyzing event time observations. Biometrics. 2018;74(2):694-702. doi:10.1111/biom.12770
- Moertel CG, Fleming TR, Macdonald JS, et al. Levamisole and fluorouracil for adjuvant therapy of resected colon carcinoma. N Engl J Med. 1990;322(6):352-358. doi:10.1056/NEJM199002083220602
- Shaw PA, Gruber S, Williamson BD, Desai R, Shortreed SM, Krakauer C, Nelson JC, van der Laan MJ. A cautionary note for plasmode simulation studies in the setting of causal inference. Stat Med. 2026;45(18-19). doi:10.1002/sim.70676
- Morris TP, White IR, Crowther MJ. Using simulation studies to evaluate statistical methods. Stat Med. 2019;38(11):2074-2102. doi:10.1002/sim.8086
6.Review
Automated review: Minor revision (novelty 4, rigor 4, clarity 5, reproducibility 5).
| Novelty 4/5 | Rigor 4/5 | Clarity 5/5 | Reproducibility 5/5 |
The revision addresses every one of the six required changes and does so by adding precision rather than by retreating from supported claims. The two interpretive over-reaches that drove my previous recommendation are gone: the size-inflation recommendation is now explicitly bounded to the bootstrap design that produced it, with the tie confound named at the point of statement, and the D1-to-D2 contrast is now correctly presented as a comparison between two resampling populations rather than as balance being switched off, with that caution repeated in the table notes where the decomposition is read. The reporting gap is closed - M4 is in Table 4, the RMST estimators have their own Table 5, and the one bias figure singled out in the text is now visible in a table. I re-verified the manuscript against the experiment record line by line and found no discrepancy in the dataset, the provenance, the 594-patient sample, or any of the reported quantities; the data are consistently and correctly described as real patient records. What remains unresolved - the cause of the size inflation, and the fact that this is one trial at one effect size - is inherent to the design, is now disclosed rather than glossed, and is directed at the right follow-up experiments. This is a careful, honest and reproducible paper reporting a refutation of its own hypothesis, and it is suitable for publication after routine copy-editing.
Strengths
- All six required changes have been made substantively, and none of them by weakening a claim the data support. The over-reaching practice recommendation about the adjusted Wald test's size is now qualified where it is made, naming the with-replacement bootstrap, the duplicated patients, the Breslow tie handling, and the untested Efron, score and likelihood-ratio alternatives, and it is reframed as a reason to check size in one's own setting rather than a general finding.
- Section 3.5 now distinguishes the mechanism of the size inflation from its cause. The manuscript states plainly that the gap between the model-based and empirical standard error is the inflation restated, and names both candidate causes - finite-sample bias of the observed information with eight nuisance parameters, and the tie burden from resampling with replacement - as unseparated by this study. This is the right level of honesty for a finding the design cannot resolve, and the Extensions paragraph now points at the experiment that would resolve it.
- The D2 caution is properly placed. A dedicated paragraph in Section 3.2 states that allocating both arms to the pooled tertile proportions changes each arm's covariate distribution, so D2 is a different resampling population from D1 rather than D1 with imbalance switched off, and attributes the -0.3854 to -0.3243 shift in the marginal estimand to that change of population. The caution is carried into the notes of Tables 1, 2 and 6, so a reader meeting the decomposition cannot mistake it for a clean within-population counterfactual.
- Reporting is now complete against house convention. Table 4 carries M4 alongside M2 and M3 for all six cells, so the 0.0068 bias figure quoted in the text is visible in a table; the new Table 5 gives bias, empirical and model standard errors, RMSE and coverage for both RMST estimators in days across all six cells. Every quantitative result in the manuscript now appears in a numbered table with notes.
- I re-checked the full manuscript against the experiment record after revision. The dataset and provenance, the filters, the 594-patient analysed sample (289 versus 305, 281 deaths, 52.7% censored), all rejection rates and paired differences with their MCSEs, the estimation performance in Tables 4 and 5, the decomposition in Table 6 and the full-sample analysis in Table 7 all match the recorded results. No number was altered in revision, and no number appears that is not in the record.
- The added scope note on the Kaplan-Meier extrapolation count is a good-faith correction I had not asked for: the manuscript now says the count was recorded at 5 years and not at 6.5, rather than asserting the stronger claim for both restriction times.
- Section 4 now opens by saying the illustrative analysis is the finite-sample realisation of D1 on the same 594 patients and not an out-of-sample application, which removes the only remaining way a reader could over-read it.
- The core contribution is unchanged and remains the paper's strength: a plasmode design whose data-generating mechanism is a real randomised trial, exact weighted-fit reference values inside each resampling population, paired discordance-based Monte Carlo standard errors, code validated against statsmodels to 1e-13, and an honestly reported refutation of the authors' own hypothesis.
Weaknesses
- The cause of the adjusted Wald test's size inflation is still unresolved. The manuscript now says so clearly and does not build on it, so this is a limit of the study rather than a defect of the paper, but it does mean the most practically arresting secondary finding cannot be acted on.
- The study remains one trial, one endpoint, one covariate set and one effect magnitude (hazard ratio 0.68), so the headline power differences are single points. The paper is explicit about this in both the limitations and the extensions, and does not generalise beyond it, but a reader wanting to know when adjustment would pay in their own trial gets the inequality kappa > rho and no calibration of it.
- Tables 4 and 5 are dense at 18 and 12 rows. This is a consequence of my own required change and is the correct trade against completeness, but the journal's production editor may wish to move the D3 blocks to supplementary material.
- Minor: the abstract at 249 words leaves no margin, and 'plasmode' appears in the title and keywords without being defined until Section 3.2. Neither affects the science.
Required changes
- No further substantive change is required. At copy-editing, consider defining 'plasmode' at first use in the introduction rather than in Section 3.2, and consider moving the D3 blocks of Tables 4 and 5 to supplementary material if space is tight.
Record
§Event timeline
- Run createdQuestion: “Does prespecified covariate adjustment in the Cox model really increase power over the unadjusted log-rank test in a randomised survival trial? A real-data resampling study of the NCCTG adjuvant colon cancer trial” · output language: English · venue: Biostatistics · Statistics in Medicine · models: agent:claude-opus-5
- Stage started · Literature reviewAttempt 1 · agent:claude-opus-5
- awaiting_agent · Literature reviewsearch response for literature_review: literature_review-search-1.response.json
- Resume requested
- Stage started · Literature reviewAttempt 2 · agent:claude-opus-5
- Search complete · Literature review18 sources · 9,187 characters of notes
- awaiting_agent · Literature reviewcomplete_json response for literature_review: literature_review-complete_json-1.response.json
- Resume requested
- Stage started · Literature reviewAttempt 3 · agent:claude-opus-5
- Search complete · Literature review18 sources · 9,187 characters of notes
- Stage completed · Literature review
- Stage started · IdeationAttempt 1 · agent:claude-opus-5
- awaiting_agent · Ideationcomplete_json response for ideation: ideation-complete_json-1.response.json
- Resume requested
- Stage started · IdeationAttempt 2 · agent:claude-opus-5
- Awaiting approval · Ideation
- Approved · IdeationIdea 1 selected
- Stage started · ExperimentationAttempt 1 · agent:claude-opus-5
- Dataset provisioned · Experimentationcolon_cancer_trial · 1,858 observations · 16 variables · sha256 6f3472a64f69
- awaiting_agent · Experimentationcomplete response for experimentation: experimentation-complete-1.response.txt
- Resume requested
- Stage started · ExperimentationAttempt 2 · agent:claude-opus-5
- Dataset provisioned · Experimentationcolon_cancer_trial · 1,858 observations · 16 variables · sha256 6f3472a64f69
- awaiting_agent · Experimentationcomplete response for experimentation: experimentation-complete-1.response.txt
- Resume requested
- Stage started · ExperimentationAttempt 3 · agent:claude-opus-5
- Dataset provisioned · Experimentationcolon_cancer_trial · 1,858 observations · 16 variables · sha256 6f3472a64f69
- Experiment round · ExperimentationRound 1: exit 0; result parsed (50.706s)
- Stage completed · Experimentation
- Stage started · AnalysisAttempt 1 · agent:claude-opus-5
- awaiting_agent · Analysiscomplete_json response for analysis: analysis-complete_json-1.response.json
- Resume requested
- Stage started · AnalysisAttempt 2 · agent:claude-opus-5
- Stage completed · Analysis
- Stage started · Paper writingAttempt 1 · agent:claude-opus-5
- awaiting_agent · Paper writingcomplete response for paper_writing: paper_writing-complete-1.response.txt
- Resume requested
- Stage started · Paper writingAttempt 2 · agent:claude-opus-5
- Stage completed · Paper writing
- Stage started · ReviewAttempt 1 · agent:claude-opus-5
- awaiting_agent · Reviewcomplete_json response for review: review-complete_json-1.response.json
- Resume requested
- Stage started · ReviewAttempt 2 · agent:claude-opus-5
- Awaiting approval · Review
- Sent back for revision · ReviewRe-running from Paper writing: “Referee recommends MAJOR REVISION (novelty 4, rigor 4, clarity 4, reproducibility 5). The manuscript matches the executed record exactly and no new computation is required; every quantity needed is al”
- Stage started · Paper writingAttempt 3 · agent:claude-opus-5
- awaiting_agent · Paper writingcomplete response for paper_writing: paper_writing-complete-1.response.txt
- Resume requested
- Stage started · Paper writingAttempt 4 · agent:claude-opus-5
- Stage completed · Paper writing
- Stage started · ReviewAttempt 1 · agent:claude-opus-5
- awaiting_agent · Reviewcomplete_json response for review: review-complete_json-1.response.json
- Resume requested
- Stage started · ReviewAttempt 2 · agent:claude-opus-5
- Awaiting approval · Review
- Approved · Review
- Run completereport=/private/tmp/claude-501/-Users-haodingresearch-Desktop-auto-research/579004ae-5819-417d-918c-9d9409997567/scratchpad/studies/biostatistics-en/runs/8ef59f2d4a2f/REPORT.md
- Resume requested
- Run completereport=/private/tmp/claude-501/-Users-haodingresearch-Desktop-auto-research/579004ae-5819-417d-918c-9d9409997567/scratchpad/studies/biostatistics-en/runs/8ef59f2d4a2f/REPORT.md