--- title: "Introduction to RFmstate" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to RFmstate} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) # One configuration is used throughout the vignette. These values keep the # source build practical; they are not inferential adequacy recommendations. canonical_seed <- 42L canonical_n <- 300L canonical_trees <- 200L canonical_min_events <- 3L canonical_sparse_warning <- 20L canonical_covariates <- c("age", "sex", "BMI", "treatment") ``` ## Overview **RFmstate** fits clock-reset cause-specific random survival forests for acyclic, non-recurrent multistate processes. For each transient state, competing exits are modeled by separate forests. Patient/profile entry-conditioned state probabilities are assembled from predicted cumulative hazards by semi-Markov entry-mass and sojourn convolution. The package also provides calendar-time Aalen-Johansen point estimates as a covariate-free descriptive baseline. The supported one-row-per-subject contract uses a common initial state, one recorded entry per state, baseline covariates, right censoring, and competing exits; it does not support left truncation, recurrent visits, directed cycles, or time-dependent covariates. The package provides: - State space and transition structure definition - Wide-to-long data conversion for counting-process format - Cause-specific random forest fitting per origin state - Entry-conditioned state probabilities via clock-reset semi-Markov convolution - Aalen-Johansen calendar-time point estimation (covariate-free baseline) - Per-transition feature importance - Genuine edge OOB concordance and patient-level cross-validated IPCW scoring - Comprehensive visualizations ## Quick Start ### 1. Define the Multistate Structure ```{r define-states} library(RFmstate) # Use the built-in clinical trial structure ms <- clinical_states() print(ms) ``` Or define another supported single-root, acyclic, non-recurrent structure: ```{r custom-states, eval=FALSE} # A simple 3-state illness-death model ms_simple <- define_multistate( state_names = c("Healthy", "Sick", "Dead"), absorbing = "Dead", transitions = list( Healthy = c("Sick", "Dead"), Sick = c("Dead") ) ) # A 4-state model with recovery ms_recovery <- define_multistate( state_names = c("Healthy", "Sick", "Recovered", "Dead"), absorbing = "Dead", transitions = list( Healthy = c("Sick", "Dead"), Sick = c("Recovered", "Dead"), Recovered = c("Dead") ) ) ``` The same workflow applies to a validated DAG with one common initial state and at least one absorbing state. Cycles and recurrent visits are rejected. ### 2. Simulate Data ```{r simulate} dat <- sim_clinical_data( n = canonical_n, structure = ms, seed = canonical_seed ) head(dat) ``` ### 3. Prepare Multistate Data Convert wide-format data to long format: ```{r prepare} msdata <- prepare_data( data = dat, id = "ID", structure = ms, time_map = list( Responded = "time_Responded", Unresponded = "time_Unresponded", Stabilized = "time_Stabilized", Progressed = "time_Progressed", Death = "time_Death" ), censor_col = "time_censored", covariates = canonical_covariates ) print(msdata) head(msdata) ``` `print(msdata)` is a concise validation summary in the state-definition order; `head(msdata)` displays the first six ordinary data rows. ### 4. Aalen-Johansen Nonparametric Baseline Compute the covariate-free calendar-time point-estimate benchmark: ```{r aj} aj <- aalen_johansen(msdata) print(aj) ``` ```{r aj-plot, fig.cap="State occupation probabilities from Aalen-Johansen estimator"} plot(aj, type = "state_occupation") ``` The figure is produced by the immediately preceding `plot()` call and shows occupation from the recorded common baseline. It has no confidence band. ```{r aj-hazard, fig.cap="Nelson-Aalen cumulative hazards"} plot(aj, type = "cumulative_hazard") ``` This second figure shows Nelson--Aalen cumulative cause-specific hazards. Requested destination states can be selected with `states =`. ### 5. Fit Random Forest Model ```{r fit} fit <- rfmstate( msdata, num.trees = canonical_trees, min_events = canonical_min_events, sparse_warning = canonical_sparse_warning, seed = canonical_seed ) print(fit) ``` No covariate vector is repeated here: `rfmstate(covariates = NULL)` uses the explicit predictor contract stored by `prepare_data()`. An explicit vector may select a nonempty subset of that contract, but it cannot add structural, outcome-time, censoring, ID, or arbitrary long-format columns. The fitted schema is rebuilt from the rows used for the actual fit. Only the documented ranger whitelist can be forwarded through `...`; sampling settings that leave no genuine OOB observations are rejected. ### 6. Model Summary ```{r summary} summary(fit) ``` The summary reports the exact fit controls and separate ranger OOB error and OOB concordance for every edge, together with verified OOB coverage and separate target-event, competing-exit, and external-censoring counts. Those edge metrics are not full-state validation. ### 7. Feature Importance ```{r importance, fig.cap="Feature importance per transition"} imp <- importance(fit) print(imp) plot(imp, type = "barplot") ``` Permutation importance is the transition-specific change in ranger OOB predictive loss after permuting a predictor. Negative values can arise from Monte Carlo noise, sparse events, correlated predictors, or irrelevant variables; they are not causal or protective effects. Event counts are stored beside the long-form importance values and should be considered when comparing edges. ```{r importance-heat, fig.cap="Feature importance heatmap"} plot(imp, type = "heatmap") ``` The heatmap contains the same edge-specific values as the preceding bar plot. ### 8. Predict for New Patients ```{r predict, fig.cap="Predicted state occupation for two patient profiles"} newdata <- data.frame( age = c(50, 70), sex = c(0, 1), BMI = c(24, 32), treatment = c(1, 0) ) prediction_horizon <- min(fit$max_duration_by_origin) pred <- predict(fit, newdata = newdata, times = seq(0, prediction_horizon, length.out = 37)) # Plot for patient 1 (young, treated) plot(pred, type = "state_occupation", subject = 1) # Plot for patient 2 (older, untreated) plot(pred, type = "state_occupation", subject = 2) ``` Both curves come from the same `pred` object and canonical fit. They are conditional on fresh entry into the initial state at elapsed duration zero; the public starting-state dimension contains only that requested state. They are not ongoing-sojourn dynamic predictions and have no confidence bands. ### 9. Diagnostics ```{r diagnostics} diag <- diagnose(fit) print(diag) ``` ```{r diag-concordance, fig.cap="Concordance index per transition"} plot(diag, type = "concordance") ``` This figure visualizes genuine ranger OOB concordance separately for each binary edge endpoint. Full-state Brier scores require patient-level cross-validation and refitting; they are never assembled from incompatible edge-level OOB predictions. Every fold rebuilds its predictor schema from training subjects only. A validation- only factor level stops the procedure rather than leaking full-data levels, and successful results retain exact subject assignments and refit seeds: ```{r diag-cv, eval=FALSE} cv_diag <- diagnose(fit, method = "cv", folds = 5, eval_times = seq(0, prediction_horizon * 0.8, length.out = 9)) plot(cv_diag, type = "brier") ``` ### 10. Transition Diagram ```{r diagram, fig.cap="Transition diagram with event counts"} plot_transition_diagram(ms, msdata) ``` The diagram uses the original display order and annotates each allowed edge with its observed event count. ## Advanced probability assembly `compute_trans_prob()` is the advanced public route for combining a complete, named set of clock-reset cumulative cause-specific hazard curves. The same validated solver is used by `predict.rfmstate()`. ```{r direct-probability} simple_ms <- define_multistate(c("A", "B"), "B", list(A = "B")) elapsed_grid <- seq(0, 2, length.out = 2001) simple_hazards <- list( "A->B" = data.frame(time = elapsed_grid, hazard = 0.4 * elapsed_grid) ) simple_prob <- compute_trans_prob( simple_hazards, simple_ms, times = c(0, 1, 2), target_grid_points = 512 ) simple_prob$state_occ ``` The output rows correspond to the requested elapsed durations and the columns to occupied states. The solver evaluates cumulative hazards as step functions, checks probability mass, and refines a regular grid without clipping or row normalization. ## Input and prediction edge cases - Left truncation/delayed entry and `s != 0` are unsupported. - Cycles, recurrent visits, tied/decreasing transition times, events after censoring or absorption, and nonpositive durations are errors. - Fitting covariates must be complete. Prediction profiles must contain every stored predictor with compatible classes, finite values, and no unseen factor levels. - Prediction times must lie within the conservative follow-up support. The explicit `extrapolate = "flat"` sensitivity option assumes zero additional hazard beyond support and is unsuitable for primary reported analyses. ## Methodology ### Separate probability constructions RFmstate forests use duration since fresh entry into the current state. Their predicted cause-specific cumulative hazards are combined by semi-Markov entry-mass and sojourn convolution on a validated regular duration grid. The output is an entry-conditioned state-occupation array, not a general Markov $P(s,t)$ matrix. The Aalen-Johansen baseline is separate: it uses calendar-time risk sets and a product integral from the recorded common study origin. ### Aalen-Johansen Estimator (Nonparametric Baseline) The Aalen-Johansen (AJ) estimator uses calendar-time risk sets and a product integral from the common baseline. RFmstate exposes point estimates as a descriptive population benchmark; it does not use AJ as the covariate-free form of the clock-reset forest solver. It estimates hazard increments via the Nelson--Aalen formula: $$d\hat{A}_{hj}(u) = \frac{dN_{hj}(u)}{Y_h(u)}$$ where $dN_{hj}(u)$ counts the observed $h \to j$ transitions at time $u$ and $Y_h(u)$ is the number at risk in state $h$ just before time $u$. This provides population-level transition probabilities without covariate adjustment and serves as a covariate-free baseline in the package. ### Random Forest Multistate Approach For covariate-adjusted predictions, we decompose the multistate model into per-origin-state competing risks problems: 1. **For each transient state** $h$, identify all outgoing transitions 2. **Fit a cause-specific RSF**: For each destination state $j$, fit a random survival forest treating transition $h \to j$ as the target event. Other observed exits end the origin-state risk interval and receive a non-target indicator; they are competing events, not loss to follow-up 3. **Extract cumulative hazards**: Use ranger's predicted cumulative hazard for each declared edge on its observed duration support 4. **Assemble by convolution**: combine state-entry masses, origin-state sojourn survival, and cause-specific next-exit distributions in topological order This approach leverages the flexibility of random forests to capture nonlinear covariate effects and interactions while maintaining the interpretability of the approved acyclic, non-recurrent multistate scope. Analytic, probability-invariant, and grid-refinement checks validate the numerical approximation without clipping or row normalization. ### Diagnostics - **OOB Error**: Out-of-bag prediction error from the random forest ensemble - **Edge OOB C-index**: genuine ranger OOB concordance for each binary cause-specific endpoint; it is not a full-model validation score - **Full-state Brier/IBS**: IPCW scores from patient-level held-out complete pipeline predictions ## References - Aalen, O.O. & Johansen, S. (1978). An empirical transition matrix for non-homogeneous Markov chains based on censored observations. *Scandinavian Journal of Statistics*, 5(3), 141-150. - Ishwaran, H. et al. (2008). Random survival forests. *Annals of Applied Statistics*, 2(3), 841-860. - Putter, H., Fiocco, M. & Geskus, R.B. (2007). Tutorial in biostatistics: Competing risks and multi-state models. *Statistics in Medicine*, 26, 2389-2430. ## Reproducibility This vignette renders from its source in a clean package checkout. It uses no external `comparison_results.rds`, private cache, or precomputed numerical result. Every displayed table and figure is generated by the code block that immediately precedes it using the canonical configuration declared in the hidden setup chunk. `sessionInfo()` records the rendering environment below. ```{r session-info} sessionInfo() ```