################################################################################ # R-code with functions implementing the penalised S-estimators used in the # paper: "S-Estimation for Penalized Regression Splines" # published in the Journal of Computational and Graphical Statistics # # Kukatharmini Tharmaratnam, Gerda Claeskens, Christophe Croux, and Matias # Salibian-Barrera. # This file also includes a comparison with penalized least squares and # penalized M-estimation methods. # An example script is included at the end of this file. ################################################################################ # # # Define the function for Penalized S-estimators pen.s <- function(y, X, N, D, lambda, num.knots, p, beta1, Sbeta1, cc=1.54764, b=.5, epsilon=1e-6) { betahats <- matrix(ncol=num.knots+2+p-1+1,nrow=N) #store the values in a matrix betahats[1,] <- c(beta1,Sbeta1) beta <- beta1 for (i in 2:N) { #update Sbeta conditional on beta r <- as.vector(y-X%*%beta) Sbeta <- s.scale(r, cc=cc, b=b, N, ep=1e-4) rs <- r / Sbeta Wbeta <- Psi(rs, cc) / rs taubeta <- n*(Sbeta)^2 / sum( r^2 * Wbeta ) #update beta conditional on Sbeta from above beta <- solve(t(X*Wbeta)%*%X +(D*lambda/taubeta))%*% t(X*Wbeta)%*% y betahats[i,] <- c(beta,Sbeta) ifelse(((norm(betahats[i,]-betahats[i-1,])/norm(betahats[i-1,])) ep ) && (it < max.it) ) { it <- it + 1 s0 <- s1 s1 <- s0*mean(Rho(r/s0,cc=cc))/b } return(s1) } # Define robust generalized cross validation function for S-penalized regression rgcv <- function(uu, y, X, D, lambda) { # uu has the fit returned by pen.s() # y is the response vector # X is the big design matrix # D is the penalty matrix # lambda is the value of the penalty constant to be evaluated n <- length(y) nw <- sum( uu$weights > 0 ) r <- as.vector(y - X %*% uu$estimates ) aa <- n * uu$scale^2 / sum( r^2 * uu$weights ) sw <- sqrt(uu$weights) H <- (X*sw)%*%solve(t(X*uu$weights)%*%X+lambda/aa*D)%*%t(X*sw) return(nw * sum( r^2 * uu$weights ) / (nw - sum(diag(H)))^2 ) } #Define rho function Rho<- function(x, cc) { U <- x/cc U1 <- 3 * U^2 - 3 * U^4 + U^6 U1[abs(U) > 1] <- 1 return(U1) } #Define psi function Psi<-function(x, cc) { U <- x/cc U1 <- 6/cc * U * (1 - U^2)^2 U1[abs(U) > 1] <- 0 return(U1) } #Define norm function norm <- function(a) sqrt(sum(a^2)) ###### # Comparison with penalized least squares estimation. ###### # Define the function for Penalized LS-estimators pen.ls <- function(y, X, D, lambda) { beta.ls <- as.vector(solve( t(X) %*% X + lambda * D ) %*% t(X) %*% y ) Sbeta.ls <- mad( y - X %*% beta.ls) return(list(beta=beta.ls,Sbeta=Sbeta.ls)) } # Define generalized cross validation function for LS-penalized regression gcv <- function(y, X, D, lambda) { # y is the response vector # X is the big design matrix # D is the penalty matrix # lambda is the value of the penalty constant to be evaluated tmp <- solve( t(X) %*% X + lambda * D ) %*% t(X) beta <- as.vector( tmp %*% y ) n <- length(y) r <- as.vector(y - X %*% beta) H <- X %*% tmp return( n * sum( r^2 ) / (n - sum(diag(H)))^2 ) } # GCV search for penalized LS-estimators pen.ls.gcv <- function(y, X, D, lambdas) { ll <- length(lambdas) # GCVs for the LS estimator gcvs <- rep(0, ll) for(i in 1:ll) { gcvs[i] <- gcv(y, X, D, lambdas[i]) } # find the best lambda lam <- max( lambdas[ gcvs == min(gcvs) ] ) beta.ls <- as.vector(solve( t(X) %*% X + lam * D ) %*% t(X) %*% y ) Sbeta.ls <- mad( y - X %*% beta.ls) # get the LS estimated mean yhat.ls <- as.vector( X %*% solve( t(X) %*% X + lam * D ) %*% t(X) %*% y ) return(list(beta=beta.ls, Sbeta=Sbeta.ls, yhat = yhat.ls, lam=lam, gcv=min(gcvs))) } ###### # Comparison with penalized M-estimation. ###### # Define the function for penalized M-estimators for fixed lambda - (Proposed by # Oh-Lee 2007) pen.m<- function(y, X, N, D, lambda, num.knots, p, epsilon=1e-6) { results <- matrix(ncol=n+1,nrow=N) #store the values in matrix # start with penalized LS tmp <- pen.ls(y, X, D, lambda) beta1 <- as.vector( tmp$beta ) mhat1 <- as.vector( X %*% tmp$beta ) sigma1 <- tmp$Sbeta results[1,] <- c(mhat1, sigma1) mhat <- mhat1 mbetaresults <- matrix(ncol=num.knots+2+p-1, nrow=N) mbetaresults[1,] <- c(beta1) mbeta <- beta1 for (j in 2:N) { res <- as.vector(y-X%*%mbeta) # sigma <- 1.4826*median(abs(res)) sigma <- mad(res) cval <- 1.345*sigma psi1 <- ifelse( abs(res)<=cval, 2*res, 2*cval*sign(res) ) z <- mhat + (psi1/2) mbeta <- solve( t(X)%*%X + D*lambda ) %*% t(X) %*% z mhat <- as.vector( X %*% solve( t(X)%*%X + D*lambda ) %*% t(X) %*% z ) results[j,] <- c(mhat,sigma) mbetaresults[j,] <- c(mbeta) ifelse(((norm(mbetaresults[j,]-mbetaresults[j-1,])/norm(mbetaresults[j- 1,]))