--- title: "Sparse and survival SDR: the frozen thesis engine" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Sparse and survival SDR: the frozen thesis engine} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") library(risdr) ``` ## Choosing the interface Version 0.4.0 extends the existing `risdr` package. `fit_risdr()` continues to use the continuous-response engine published in 0.3.1, including its component argument lists, defaults, prediction, and cross-validation tools. The additional engine has explicit entry points: | Entry point | Purpose | |:--|:--| | `fit_risdr_sparse()` | Joint dimension and sparsity selection | | `fit_risdr_sparse_realdata()` | Outcome cleaning and variance screening | | `fit_risdr_dual()` | Sample-space PCA and mapping to predictor coordinates | The sparse engine accepts continuous, categorical, and right-censored survival responses. It requires covariance stabilisation and uses C1F complexity. It does not replace the existing C1/C1F continuous-response selector. The locally frozen thesis package was versioned 0.3.2. Its `fit_risdr()` calls map to `fit_risdr_sparse()` in this release, and its `fit_risdr_realdata()` calls map to `fit_risdr_sparse_realdata()`. The numerical engine is preserved; private helper names and the sparse fit classes isolate it from the existing API. The original thesis results remain associated with the frozen 0.3.2 sources. ## A reproducible censored-survival example This example uses synthetic data and does not reproduce the thesis cohort. ```{r data} set.seed(2026) X <- matrix(rnorm(100 * 8), nrow = 100) colnames(X) <- paste0("Gene", seq_len(ncol(X))) event_time <- rexp(100, rate = exp(0.5 * X[, 1] - 0.3 * X[, 2])) censor_time <- rexp(100, rate = 0.5) time <- pmin(event_time, censor_time) delta <- as.integer(event_time <= censor_time) table(delta) ``` The event indicator is 1 for an observed event and 0 for a right-censored record. Time units must be consistent and recorded by the analyst. Four quantile slices are formed within each censoring-status group, yielding up to eight occupied slices. The engine uses these labels in SIR, SAVE, and canonical pairwise-slice DR. This slicing convention is part of the frozen methodology; it does not by itself establish robustness to informative censoring. ```{r fit} fit <- fit_risdr_sparse( X, time, delta = delta, response_type = "survival", sdr_method = "sir", cov_method = "oas", nslices = 4, d_max = 3, selector = "cicomp", lambda_grid = c(0, 0.05, 0.1), support_penalty = FALSE ) c(d = fit$d, lambda = fit$best_lambda) fit$selection_grid ``` For each dimension and lambda, a Cox model is fitted to the corresponding reduced predictors using Efron ties. Write its partial log likelihood as `ell`, and the effective count as `k`. With the thesis default `support_penalty = FALSE`, `k = d` for survival outcomes. The criteria are: - AIC: `-2 * ell + 2 * k`; - BIC: `-2 * ell + k * log(n)`; - CAIC: `-2 * ell + k * (log(n) + 1)`; - ICOMP: `-2 * ell + 2 * k + 2 * C1F`; - CICOMP: `-2 * ell + k * (log(n) + 1) + 2 * C1F`. C1F is computed from the covariance of the reduced predictors. The final pair minimises the requested criterion across the candidate grid. An explicitly fixed `d` instead selects lambda within that dimension. Failed candidates are recorded in the grid. All comparisons must use compatible outcomes and samples. ## Variable selection Adaptive weights distribute C1F over predictor coordinates using squared covariance eigenvector entries. They are normalised to mean one. Weighted soft thresholding operates on the direction entries. The frozen safeguard restores a dense direction if thresholding removes it entirely, so support size need not decrease monotonically over every lambda grid. ```{r support} B <- fit$directions support <- data.frame(variable = rownames(B), norm = sqrt(rowSums(B^2))) support <- support[support$norm > 1e-8, , drop = FALSE] support[order(support$norm, decreasing = TRUE), , drop = FALSE] ``` The screened wrapper applies its variance filter after cleaning incomplete outcomes. Its `selected_variables` table reports the active row norms of the selected direction matrix. A nonzero loading or a high rank is a descriptive selection result, not a significance test or a validated biological marker. ## The dual workflow ```{r dual} dual <- fit_risdr_dual( X, time, delta = delta, response_type = "survival", dual_rank = 5, d_max = 3, sdr_method = "sir", cov_method = "oas", lambda_grid = c(0, 0.05, 0.1), gene_select_top = 4, verbose = FALSE ) dual$selected_variables ``` The dual path forms the sample-space Gram matrix, avoiding a full predictor covariance. It retains PCA scores and fits the sparse engine in that score space. The inner standardisation is undone before mapping selected directions through PCA loadings. The ranking uses the row Euclidean norms of those selected mapped directions. Dense-direction mappings are separately retained. Sparsity among retained PCA coordinates does not generally yield zero loadings for original predictors. `gene_select_top` and `gene_select_quantile` therefore define a ranking rule and must be reported separately from coordinate sparsity. ## Prediction and diagnostics ```{r prediction} predict(fit, X[1:2, , drop = FALSE], type = "scores") predict(fit, X[1:2, , drop = FALSE], type = "risk") predict(dual, X[1:2, , drop = FALSE], type = "scores") ``` Survival predictions are Cox linear predictors or relative risks, not survival times. Training predictor names are checked and reordered; the training centres and scales are reused. The prediction adapters do not change the frozen fits. ```{r ph} survival::cox.zph(fit$downstream_fit, transform = "km", terms = TRUE, global = TRUE) ``` Proportional-hazards diagnostics apply to the downstream model actually fitted. A small diagnostic p value indicates evidence of departure from proportional hazards; a nonsignificant value is not proof of the assumption. The model above uses all selected coordinates. A diagnostic for a separate first-coordinate model addresses that separate model. Apparent in-sample criteria, concordance, and Kaplan-Meier separation do not constitute external validation. For out-of-sample assessment, repeat all outcome-dependent selection and preprocessing within training partitions. The existing continuous-response cross-validation utilities do not evaluate the new survival engine. An independent cohort remains necessary for external validation of a particular application.