Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • New package on SSC: -finegray- Fast Fine-Gray competing risks regression

    I'd like to announce finegray, a new package for Fine-Gray competing risks regression, now available from SSC.

    To install:
    Code:
        ssc install finegray
    finegray fits the Fine and Gray (1999) subdistribution hazard model for competing risks data. It estimates subdistribution hazard ratios (SHR) that quantify the effect of covariates on the cumulative incidence function in the presence of competing events.

    The implementation uses a Mata forward-backward scan algorithm (Kawaguchi et al. 2021) that avoids data expansion entirely. It produces identical point estimates and log-likelihoods to stcrreg but runs much faster, especially on larger datasets. It is roughly 40x faster at N=500 and 650x faster at N=10,000 (see finegray help in Stata for more on this).

    The package includes three commands:
    - finegray: Fine-Gray subdistribution hazard regression
    - finegray_predict: Post-estimation predictions (linear predictor, CIF, Schoenfeld residuals)
    - finegray_phtest: Test proportional subdistribution hazards assumption

    Features include full factor variable support (i., c., ##), stratified censoring distributions, clustered and model-based standard errors, left-truncated (delayed entry) data, CIF prediction at arbitrary time points, and compatibility with margins.

    Quick example:
    Code:
        webuse hypoxia, clear
        gen byte status = failtype
        stset dftime, failure(dfcens==1) id(stnum)
        finegray ifp tumsize pelnode, compete(status) cause(1)
        finegray_predict cif_hat, cif
        finegray_phtest
    The package has been cross-validated against stcrreg, R's cmprsk::crr, and R's fastcmprsk::fastCrr, confirming agreement in coefficients, standard errors, log-likelihoods, and cumulative incidence functions.

    Requires Stata 16 or later.

    References:
    Fine JP, Gray RJ. A proportional hazards model for the subdistribution of a competing risk. JASA 1999; 94(446): 496-509.
    Kawaguchi ES, Shen JI, Suchard MA, Li G. Scalable algorithms for large competing risks data. Journal of Computational and Graphical Statistics 2021; 30(3): 685-693.

    If you find any bugs or have any feature requests, please submit them at https://github.com/tpcopeland/Stata-Tools/issues.

    Also, please feel free to offer feedback on other packages in my Stata-Tools GitHub. I plan on releasing some of the ones that are most useful on SSC in the near future when I feel I've adequately tested them. In particular, I feel the following packages might be of interest to many of you: tabtools, tvtools, codescan, consort, datamap, and eplot.

  • #2
    Great stuff, thank you Dr Copeland.
    I suggested some ideas for the package on GitHub -- not sure if I did it correctly, first time on there!

    Comment


    • #3
      Thank you for the new package which is very fast compared to stccrreg. When one uses the "finegray_predict cif_hat, cif " command such as in the example provided above, i am confused about what time point this prediction relates to. For instance, is it the predicted CIF after t=1, t=2, t=3, etc? For stcrreg, the standard approach is to use "predict, basecif" command/option to predict the baseline CIF at time points observed in the dataset. From here one can calculate the predicted CIF for each participant/individual in the dataset for relevant timepoints. In the documentation for the new "finegray_predict" command, there doesnt seem to be any reference to what time point the predicted CIF relates to. Can you clarify? apologies if i am missing something obvious.

      Comment


      • #4
        First, my apologies for the slow reply here! I've turned on the email notifications for Statalist replies. Thank you all for your patience, and thank you for the thoughtful questions and suggestions. They shaped my updated release. I responded in much greater detail to Jeffrey's and Hamish's specific questions on the GitHub issue tracker, and I'd point anyone following along there for examples and the methodological reasoning: https://github.com/tpcopeland/Stata-Tools/issues/1

        The short version is that everything raised has now been addressed in finegray 1.1.0, which is live on GitHub and heading to SSC shortly.

        What's new in 1.1.0

        1. Datasets with multiple records per subject are now supported.
        The earlier version blocked these, which was more conservative than necessary. finegray now accepts subjects contributing more than one in-sample record, delayed entry / left truncation via stset (start, stop] intervals, or stsplit data, as long as the model covariates are constant within id(). It detects the multiple records, verifies the covariates are fixed within subject, checks the intervals are contiguous, and internally reduces each subject to one risk-set unit (earliest entry, latest exit, final status), printing a note as it does so. Genuinely time-varying covariates remain deliberately unsupported, because the subdistribution hazard is not well-defined with them (the same reason stcrreg has no tvc() option); in that case you get a clear message pointing to cause-specific stcox.

        2. A new CIF-curve postestimation command, finegray_cif — the stcurve, cif analogue, and then some. It plots the predicted cumulative incidence curve over the full follow-up axis for a chosen covariate profile (at(var=# ...), default the estimation-sample means), and, unlike stcurve, it can draw a pointwise confidence band (analytic influence-function based, on the complementary log-log scale, with an optional exact bootstrap()/seed() cross-check). saving() writes the numeric estimates (time cif se lci uci) to a dataset (the outfile analogue), and timepoints() evaluates on a custom grid, so the graph is never a dead end.

        3. Fixed-horizon (e.g. 5-year) CIF prediction with confidence intervals, by two routes:
        • finegray_cif, attime(1 2 3 5) ci reports a table of the CIF at each listed horizon for a covariate profile, each with a CI — the direct "5-year cumulative incidence with a CI" reportable number.
        • finegray_predict newvar, cif timevar(tvar) ci produces one column holding every subject's predicted CIF at a common horizon, holding each subject's own covariates, with per-subject confidence limits.
        4. Clarified what finegray_predict, cif predicts. To answer Hamish's question directly: by default finegray_predict cif_hat, cif evaluates each subject's predicted CIF at that subject's own analysis time _t; one covariate-adjusted prediction per subject, not a single fixed horizon and not the baseline CIF. The help file now spells this out, and timevar() lets you evaluate at any common horizon instead. The GitHub issue has the full walk-through, including how to read the baseline step function from e(basehaz).

        Installing 1.1.0 now


        Code:
         
         capture ado uninstall finegray 
        Code:
         net install finegray, from("https://raw.githubusercontent.com/tpcopeland/Stata-Tools/main/finegray") replace 
        The SSC copy is currently 1.0.0; I'll be pushing 1.1.0 to SSC within the next week or two, so an adoupdate / ssc install finegray, replace will pick it up shortly. In the meantime the GitHub install above gives you the full 1.1.0 feature set.

        Comment


        • #5
          thanks for the update. FYI I just downloaded the Github version. FYI There seem to be quite a few typos in the help file when viewed via -help finegay- (esp. in Remarks section) -- characters seem omitted

          Comment


          • #6
            Originally posted by Stephen Jenkins View Post
            thanks for the update. FYI I just downloaded the Github version. FYI There seem to be quite a few typos in the help file when viewed via -help finegay- (esp. in Remarks section) -- characters seem omitted
            Thanks Stephen, I'm actually seeing what you're seeing too, but it's a rendering problem in the help file viewer, because when I go to check the underlying .sthlp file the text is correct. For example, I see "Against stcrreg, coeffi atch within" in the help viewer, but in the actual text editing view in a regular text viewer it's correct: "Against {cmd:stcrreg}, coefficients match within"

            I'll try to figure it out before submitting to SSC.

            Comment


            • #7
              finegray 1.3.2 is now live on SSC!

              My thanks first to Kit Baum for processing both the original submission and this update. Thanks also to Jeffrey Chan for the suggestions on GitHub, to Hamish Innes for the question about what finegray_predict, cif evaluates, and to Stephen Jenkins for catching the garbled text in the help viewer. All three shaped this release.

              To update from 1.0.0:

              ssc install finegray, replace

              What has changed since 1.0.0:

              1. New command finegray_cif: the stcurve, cif analogue. It draws the predicted CIF curve for a covariate profile (at(var=# ...), default the sample means) with an analytic or bootstrap confidence band, reports the CIF at fixed horizons (attime(1 2 3 5) ci), draws one curve per group with over(), and saves the estimates with saving().

              2. finegray_predict, cif now documents what it evaluates: each subject's predicted CIF at that subject's own _t, with timevar() to use a common horizon instead. This was Hamish's question in #3.

              3. Multiple records per subject, delayed entry via (start, stop] intervals, and stsplit data are accepted, with the covariates checked for constancy within id(). As of 1.3.2, stset without id() is also accepted; each record is then one subject, as in stcrreg, and the fit is identical to the same data declared with id().

              4. Stratified baseline subdistribution hazard via bstrata() (Zhou et al. 2011), and piecewise-constant time-varying effects via tvc() with tsplit(). Both compose with each other and with the variance options.

              5. Weights: pweight and fweight. Also mi estimate, cmdok: support.

              6. Variance: robust, cluster-robust, and a nuisance-adjusted sandwich that accounts for the estimated censoring distribution, including on delayed-entry fits (Zhang, Zhang and Fine 2011). Factor-variable terms work with margins, contrast and pwcompare.

              7. Postestimation additions: baseline subhazard output, Schoenfeld residuals, and predictions on new data after estimates use.

              8. The methods, formulas and the rationale behind each refusal are in a shipped help topic: help finegray_methods.

              9. The help-viewer problem Stephen reported in #5 was not a typo in the files. The Stata Viewer drops characters at wrap boundaries when a paragraph is written as one very long source line. All help files are now wrapped to normal width and render cleanly.

              10. Several checks that used to run without any notification now stop with an error: a record with a missing compete() value is refused rather than dropped, weights that change between fit and postestimation are refused, and a prediction computable for no observation errors instead of returning all missings.

              One breaking change: estimates saved by a build before 1.3.0 are refused with r(301) and need to be refit, and e(covariates) is now e(designvars). Without the new options, e(b), e(V), e(ll) and the baseline hazard are unchanged from 1.0.0.

              Everything is cross-validated against stcrreg and against frozen references from R's cmprsk and fastcmprsk. Bug reports and feature requests remain welcome at https://github.com/tpcopeland/Stata-Tools/issues.

              Comment


              • #8
                Thanks for this - it is amazing! Our project inolves F-G with time-varying covariates. It was possible with stccreg but takes forever to run. Is it going to be possible with finegray?

                Comment


                • #9
                  Originally posted by Bree Shi View Post
                  Thanks for this - it is amazing! Our project inolves F-G with time-varying covariates. It was possible with stccreg but takes forever to run. Is it going to be possible with finegray?
                  Thanks, I'm glad you're finding it useful!

                  So the answer to your question is no, not for true time-varying covariates, but there's a reason for it, one that I only had to think through and figure out while working on this package. There are two time-varying concepts that can be applied to the competing risks context, but only one of them is interpretable: (1) time-varying effects, which are interpretable, and (2) internal time-varying covariates, which aren't.

                  Time-varying effects (supported). The covariate is fixed at baseline, but its subdistribution hazard ratio can change over follow-up. You get this with tvc() and tsplit(), which fit a piecewise-constant coefficient (one SHR per interval):

                  Code:
                  webuse hypoxia, clear
                  stset dftime, failure(dfcens==1) id(stnum)
                  
                  * effect of pelnode allowed to differ before/after t = 1
                  finegray ifp tumsize pelnode, compete(failtype) cause(1) tvc(pelnode) tsplit(1)
                  
                  * is the effect actually different across intervals?
                  test [tvc1]pelnode = [tvc2]pelnode
                  The output has a main equation for the proportional covariates and tvc1, tvc2 equations holding the interval-specific SHRs for pelnode.

                  Time-varying covariates (not supported). If your data are split into multiple records per id() and the covariate's value changes between records (e.g., a lab value or a treatment start), finegray stops with r(198) ("covariate ... varies within subject"). Multiple records are fine as long as the covariates stay constant within each subject (delayed entry, stsplit, etc.).

                  And here comes the part that I only fully grasped while making this package: For internal covariates (i.e., ones measured on the person, like a lab value or treatment), Fine-Gray doesn't really work. In Fine-Gray, people who have had the competing event aren't censored; they stay in the risk set with a weight for the rest of follow-up. So the model needs their covariate value at every later event time, and for something like a lab value or treatment status there is no such value after death: whatever you plug in (e.g., the last observed value) is an assumption, and the SHR depends on it. Austin, Latouche & Fine (2020, Stat Med 39:103-113) lay this out, including the last-value approach proposed by Beyersmann & Schumacher (2008, Biostatistics 9:765-776). Poguntke et al. (2018, BMC Med Res Methodol) found by simulation that F-G with time-dependent covariates can show an effect where none exists and give counter-intuitive results elsewhere. stcrreg will run it, but what the SHR means is very iffy depending on the context. External covariates such as age or calendar time don't have the missing-value problem, since their value is defined for everyone at every time, so they can go into a Fine-Gray model; even then, Austin et al. point out that the coefficient no longer describes the effect on the cumulative incidence the way a baseline covariate's does. finegray refuses those too for now.

                  For internal time-varying covariates, the recommendation is cause-specific hazard models, which handle time-varying covariates with no conceptual problem. Run stcox on your counting-process data once for each cause, treating the competing event as censored. Stata's ICU example data have this setup, with hospital-acquired pneumonia switching from 0 to 1 partway through the stay and discharge competing with death:

                  Code:
                  webuse pneumonia, clear
                  
                  * cause-specific Cox: death (discharge censored), then discharge (death censored)
                  collect clear
                  stset ndays, id(id) failure(died)
                  collect: stcox pneumonia
                  stset ndays, id(id) failure(discharged)
                  collect: stcox pneumonia
                  Pneumonia gives a hazard ratio of 4.30 (3.10-5.97) for death and 0.87 (0.66-1.16) for discharge. If your question is really "does the effect of a baseline covariate change over time", then tvc() above is what you want.

                  A small plug, in case it saves you time on the write-up: my tabtools package exports publication-ready tables straight to Excel. table1_tc (a fork of table1_mc which is a fork of table1) does the Table 1, and regtab takes any models you've run under collect: (like the two above) and puts them side by side:

                  Code:
                  regtab, xlsx(results.xlsx) sheet("Cause-specific") models("Death \ Discharge") coef("HR")
                  
                  * Table 1, one row per patient
                  bysort id (ndays): keep if _n == _N
                  gen byte outcome = cond(died==1, 1, cond(discharged==1, 2, 0))
                  label define outcome 0 "Still in ICU" 1 "Died" 2 "Discharged"
                  label values outcome outcome
                  table1_tc age pneumonia, by(outcome) xlsx(results.xlsx) sheet("Table 1")
                  An SSC release of tabtools is planned too, but I'm dealing with a "perfect is the enemy of done" problem, so GitHub it is for now:

                  Code:
                  net install tabtools, from("https://raw.githubusercontent.com/tpcopeland/Stata-Tools/main/tabtools") replace
                  As for finegray itself, please install from GitHub for now, since that copy has the latest version:

                  Code:
                  capture ado uninstall finegray
                  net install finegray, from("https://raw.githubusercontent.com/tpcopeland/Stata-Tools/main/finegray") replace
                  I'm planning to submit an update to SSC very soon. Once it's there, switch back to the SSC copy so you get future updates through the usual channel:

                  Code:
                  ado uninstall finegray
                  ssc install finegray
                  Relatedly, if you've already installed from SSC, uninstall before installing the GitHub version.

                  Comment

                  Working...
                  X