aboutsummaryrefslogtreecommitdiff
path: root/R/fit.sparsestep.R
diff options
context:
space:
mode:
Diffstat (limited to 'R/fit.sparsestep.R')
-rw-r--r--R/fit.sparsestep.R139
1 files changed, 139 insertions, 0 deletions
diff --git a/R/fit.sparsestep.R b/R/fit.sparsestep.R
new file mode 100644
index 0000000..ca40f43
--- /dev/null
+++ b/R/fit.sparsestep.R
@@ -0,0 +1,139 @@
+#' @title Fits the SparseStep model
+#'
+#' @description Fits the SparseStep model for a single value of the
+#' regularization parameter.
+#'
+#' @param x matrix of predictors
+#' @param y response
+#' @param lambda regularization parameter
+#' @param gamma0 starting value of the gamma parameter
+#' @param gammastop stopping value of the gamma parameter
+#' @param IMsteps number of steps of the majorization algorithm to perform for
+#' each value of gamma
+#' @param gammastep factor to decrease gamma with at each step
+#' @param normalize if TRUE, each variable is standardized to have unit L2
+#' norm, otherwise it is left alone.
+#' @param intercept if TRUE, an intercept is included in the model (and not
+#' penalized), otherwise no intercept is included
+#' @param force.zero if TRUE, absolute coefficients smaller than the provided
+#' threshold value are set to absolute zero as a post-processing step,
+#' otherwise no thresholding is performed
+#' @param threshold threshold value to use for setting coefficients to
+#' absolute zero
+#' @param XX The X'X matrix; useful for repeated runs where X'X stays the same
+#' @param Xy The X'y matrix; useful for repeated runs where X'y stays the same
+#' @param use.XX whether or not to compute X'X and return it
+#' @param use.Xy whether or not to compute X'y and return it
+#'
+#' @return A "sparsestep" object is returned, for which predict, coef, methods
+#' exist.
+#'
+#' @export
+#'
+#' @examples
+#' data(diabetes)
+#' attach(diabetes)
+#' object <- sparsestep(x, y)
+#' plot(object)
+#' detach(diabetes)
+#'
+sparsestep <- function(x, y, lambda=1.0, gamma0=1e6,
+ gammastop=1e-8, IMsteps=2, gammastep=2.0, normalize=TRUE,
+ intercept=TRUE, force.zero=TRUE, threshold=1e-7,
+ XX=NULL, Xy=NULL, use.XX = TRUE, use.Xy = TRUE)
+{
+ call <- match.call()
+
+ nm <- dim(x)
+ n <- nm[1]
+ m <- nm[2]
+ one <- rep(1, n)
+
+ if (intercept) {
+ meanx <- drop(one %*% x)/n
+ x <- scale(x, meanx, FALSE)
+ mu <- mean(y)
+ y <- drop(y - mu)
+ } else {
+ meanx <- rep(0, m)
+ mu <- 0
+ y <- drop(y)
+ }
+
+ if (normalize) {
+ normx <- sqrt(drop(one %*% (x^2)))
+ names(normx) <- NULL
+ x <- scale(x, FALSE, normx)
+ cat("Normalizing in sparsestep\n")
+ } else {
+ normx <- rep(1, m)
+ }
+
+ if (use.XX & is.null(XX)) {
+ XX <- t(x) %*% x
+ }
+ if (use.Xy & is.null(Xy)) {
+ Xy <- t(x) %*% y
+ }
+
+ # Start solving SparseStep
+ gamma <- gamma0
+ beta <- matrix(0.0, m, 1)
+
+ it <- 0
+ while (gamma > gammastop)
+ {
+ for (i in 1:IMsteps) {
+ alpha <- beta
+ omega <- gamma/(alpha^2 + gamma)^2
+ Omega <- diag(as.vector(omega), m, m)
+ beta <- solve(XX + lambda * Omega, Xy)
+ }
+ it <- it + 1
+ gamma <- gamma / gammastep
+ }
+
+ # perform IM until convergence
+ epsilon <- 1e-14
+ loss <- get.loss(x, y, gamma, beta, lambda)
+ lbar <- (1.0 + 2.0 * epsilon) * loss
+ while ((lbar - loss)/loss > epsilon)
+ {
+ alpha <- beta
+ omega <- gamma/(alpha^2 + gamma)^2
+ Omega <- diag(as.vector(omega), m, m)
+ beta <- solve(XX + lambda * Omega, Xy)
+ lbar <- loss
+ loss <- get.loss(x, y, gamma, beta, lambda)
+ }
+
+ # postprocessing
+ if (force.zero) {
+ beta[which(abs(beta) < threshold)] <- 0
+ }
+ residuals <- y - x %*% beta
+ beta <- scale(t(beta), FALSE, normx)
+ RSS <- apply(residuals^2, 2, sum)
+ R2 <- 1 - RSS/RSS[1]
+
+ object <- list(call = call, lambda = lambda, R2 = R2, RSS = RSS,
+ gamma0 = gamma0, gammastop = gammastop,
+ IMsteps = IMsteps, gammastep = gammastep,
+ intercept = intercept, force.zero = force.zero,
+ threshold = threshold, beta = beta, mu = mu,
+ normx = normx, meanx = meanx,
+ XX = if(use.XX) XX else NULL,
+ Xy = if(use.Xy) Xy else NULL)
+ class(object) <- "sparsestep"
+ return(object)
+}
+
+get.loss <- function(x, y, gamma, beta, lambda)
+{
+ Xb <- x %*% beta
+ diff <- y - Xb
+ b2 <- beta^2
+ binv <- 1/(b2 + gamma)
+ loss <- t(diff) %*% diff + lambda * t(b2) %*% binv
+ return(loss)
+}