survFit: support models fitted with case weights#122
Open
fabian-s wants to merge 5 commits into
Open
Conversation
Use the weighted Breslow estimator of the cumulative baseline hazard instead of stopping with an error for models fitted with non-unit weights: events and risk sets are weighted by the case weights, and the baseline survivor function is computed at the weighted mean of the linear predictor (identical to the previous behaviour for unit weights). Results agree with survival::survfit for weighted coxph fits, and fits with integer case weights give results identical to fitting on the correspondingly expanded data. Closes boost-R#121 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HqB9XX7JjdQJ1vah4eyWv9
There was a problem hiding this comment.
Pull request overview
This PR extends survFit() for mboost Cox models to support non-unit case weights by implementing the weighted Breslow estimator, aligning behavior with survfit.coxph() conventions (weighted risk sets/events and weighted centering).
Changes:
- Implement weighted Breslow baseline hazard estimation and weighted centering in
survFit.mboost(). - Document the new weighted behavior in
survFit’s Rd docs and add a NEWS entry referencing #121. - Add regression tests covering integer-weight equivalence to expanded data and non-integer-weight agreement with
coxph, plus ablackboostsmoke test.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
R/survival.R |
Replaces the prior “cannot deal with weights” error with a weighted Breslow estimator and weighted centering; adds drop = FALSE to preserve matrix shape. |
man/survFit.Rd |
Documents weighted-case behavior, including that n.event becomes a weighted event count. |
inst/NEWS.Rd |
Adds a user-visible NEWS entry announcing survFit support for case-weighted fits. |
tests/regtest-glmboost.R |
Adds regression coverage for integer/non-integer weights vs expanded data / weighted coxph. |
tests/regtest-blackboost.R |
Adds a smoke test verifying survFit() works on a weighted blackboost Cox model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
30
to
+34
| of \code{object}, along with the Breslow estimator, is used for computing the | ||
| predicted survivor function for each row in \code{newdata}. | ||
|
|
||
| If the model was fitted with case weights, the weighted Breslow | ||
| estimator is used for computing the baseline survivor function, i.e., |
…behavior Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HqB9XX7JjdQJ1vah4eyWv9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
survFit()previously stopped with"survFit cannot (yet) deal with weights"for any model fitted with non-unit case weights, even though
blackboost()/glmboost()/gamboost()happily acceptweightswithfamily = CoxPH()(see #121, motivated by the tidymodels integration of mboost).
This PR implements the weighted Breslow estimator in
survFit.mboost:Σ wᵢ·eventᵢ,Σ_{tⱼ ≥ t} wⱼ·exp(ηⱼ),curve (
newdata = NULL) corresponds to the weighted "average" observation —matching
survfit.coxph()'s convention of centering at the weightedcovariate means.
For unit weights,
weighted.mean()reduces tomean()and the computation isidentical to the previous implementation (verified below). Two further
properties fall out of the estimator:
(
rescale_weights(), a constant factor) cancels in the Breslow estimator,cvrisk()-style subsampling folds)correctly drop out of both events and risk sets.
Also added
drop = FALSEwhen subsetting the survival matrix, so a fit with asingle distinct event time no longer collapses it to a vector.
Changes
R/survival.R: weighted Breslow estimator instead of the error.man/survFit.Rd: documents the weighted behaviour, incl. thatn.eventisthe weighted number of events for weighted fits.
inst/NEWS.Rd: entry referencing Feature request:survFit()with weights #121.tests/regtest-glmboost.R+.Rout.save: equivalence and coxph comparisontests (see below).
tests/regtest-blackboost.R+.Rout.save: smoke test of the scenario fromFeature request:
survFit()with weights #121 (blackboost+CoxPH()+ non-integer weights,survFiton newdata).The tests use deterministic weights on purpose: there is no
set.seed()between the survFit section and the subsequent sections in the test files, so
consuming RNG draws there would silently change all downstream expected
output in the
.Rout.savefiles.Verification (R 4.3.3, survival 3.5-8)
Issue example now works. The exact reprex from #121 (
blackboostonlungwithrunifweights,survFiton newdata) runs and returns valid,monotone survival curves.
Backward compatibility. Re-running
regtest-glmboost.Randregtest-gamboost.Ragainst the stored.Rout.savefiles withR CMD Rdiffshows zero differences outside the newly added section — e.g. the existing
unweighted comparison still prints
max(A1$surv-A2$surv)=4.99e-05exactly as before.
Integer case weights ≡ expanded data (exact). Fitting
glmboost(..., family = CoxPH(), weights = w)with integerwand fitting on therow-expanded data
ovarian[rep(1:n, w), ]giveall.equal()-identicalsurvFitresults forsurv,time,n.event, and newdata predictions.glmboost vs. weighted coxph (non-integer weights). With
wts <- seq(0.5, 1.5, length.out = nrow(ovarian)):Both are within boosting-convergence tolerance of the partial-likelihood MLE
(the same order of magnitude as the existing unweighted comparisons in the
test file,
4.99e-05/8.24e-05), and the agreement of the baselinecurve confirms the weighted-mean centering matches
survfit.coxph. Theregression tests assert both differences
< 0.001.Package checks.
R CMD INSTALLbuilds cleanly andtools::checkRd("man/survFit.Rd")passes.Closes #121
https://claude.ai/code/session_01HqB9XX7JjdQJ1vah4eyWv9