#### ---------------------- Introduction ## R functions for ## (0) making R understand a bit of GAUSS. Some of this code was ported from GAUSS. ## (1) Estimating the gradient dx/dt for a time series x(t) with observations ## at times t01) gcv<-1.e12; return(list(x=x,knots=knots,origin=origin,alpha=alpha,Boundary.knots=Boundary.knots, intercept=intercept, coef=betahat, fitted.values=yhat,gcv=gcv,enp=sum(diag(hat)))) } predict.rss<-function(model,newx){ X<-rssbasis(newx,model$knots,model$origin,model$intercept); yhat<-X%*%(model$coef); return(list(x=newx,y=yhat)) } # Fit Penalized Regression Spline with smoothing parameter chosen by GCV(p) # Hmin and Hmax are user-supplied brackets on log10(optimal smoothing parameter) # Uses golden section search so hmin and hmax MUST bracket a minimum or the # routine will return garbage. Use draw=T (the default) to make sure you have a bracket. gcv.rss<-function(xvals,yvals,hmin,hmax,nknots=NA,Boundary.knots=NA,intercept=T,penalty=1,tol=0.01,draw=T) { if(is.na(Boundary.knots)) Boundary.knots<-range(xvals) if(is.na(nknots)) nknots<-20; C<-0.61803399; R<-1-C; dh<-hmax-hmin; hvals<-hmin+0.1*c(0:10)*dh; Gvals<-matrix(0,11,1); for (i in 1:11) { Gvals[i]<-rssfit(xvals,yvals,nknots,Boundary.knots,intercept=intercept, hvals[i],penalty=penalty)$gcv; print(c(i,hvals[i],Gvals[i])); } imin<-minindc(Gvals); if((imin<=1)||(imin>=11)) {hopt<-hvals[imin]} else { ax<-hvals[imin+1]; bx<-hvals[imin]; cx<-hvals[imin-1]; x0<-ax; x3<-cx; if(abs(cx-bx)>abs(bx-ax)) {x1<-bx; x2<-bx+C*(cx-bx)} else {x2<-bx; x1<-bx-C*(bx-ax)} f1<-rssfit(xvals,yvals,nknots,Boundary.knots,intercept=intercept,penalty=penalty,alpha=x1)$gcv; f2<-rssfit(xvals,yvals,nknots,Boundary.knots,intercept=intercept,penalty=penalty,alpha=x2)$gcv; while(abs(x3-x0)>tol*(abs(x1)+abs(x2))) { if(f20); if(nact<1) { hat<-X%*%solve(XTX+(10^alpha)*D,tol=1.e-16)%*%t(X); Z<-1; } else { iact<-iact[iact>0]; Cact<-zmat[iact,]; if(nact<2) Cact<-matrix(Cact,1,cols(zmat)) Z<-Null(t(Cact)); ZT<-t(Z); hat<-X%*%Z%*%solve(ZT%*%(XTX+(10^alpha)*D)%*%Z,tol=1.e-16)%*%ZT%*%t(X); } enp<-sum(diag(hat)); d1<-sum(diag(hat))/length(y); gcv<-msr/((1-penalty*d1)^2); if(penalty*d1>1) gcv<-1.e12; return(list(x=x,knots=knots,origin=origin,alpha=alpha, intercept=intercept, coef=betahat, fitted.values=yhat,gcv=gcv, penalty=penalty,Z=Z,enp=enp)) } # Fit Monotone Penalized Regression Spline with smoothing parameter chosen by GCV(p) # Hmin and Hmax are user-supplied brackets on the log10(optimal smoothing parameter) # Uses golden section search; hmin and hmax must bracket a minimum gcv.rssM<-function(xvals,yvals,hmin,hmax,nknots=NA,Boundary.knots=NA,intercept=T,penalty=1,ncongrid=NA,tol=0.01,draw=T) { if(is.na(Boundary.knots)) Boundary.knots<-range(xvals); if(is.na(nknots)) nknots<-20; if(is.na(ncongrid)) ncongrid<-50; ex<-order(xvals); xvals<-xvals[ex]; yvals<-yvals[ex]; C<-0.61803399; R<-1-C; hvals<-hmin+0.1*c(0:10)*(hmax-hmin); Gvals<-matrix(0,11,1); for (i in 1:11) { Gvals[i]<-rssfitM(xvals,yvals,nknots,Boundary.knots,intercept=intercept, alpha=hvals[i],penalty=penalty,ncongrid=ncongrid)$gcv; print(c(i,hvals[i],Gvals[i])); } imin<-minindc(Gvals); if((imin<=1)||(imin>=11)) {hopt<-hvals[imin]} else { ax<-hvals[imin-1]; bx<-hvals[imin]; cx<-hvals[imin+1]; x0<-ax; x3<-cx; if(abs(cx-bx)>abs(bx-ax)) {x1<-bx; x2<-bx+C*(cx-bx)} else {x2<-bx; x1<-bx-C*(bx-ax)} f1<-rssfitM(xvals,yvals,nknots,Boundary.knots,intercept=intercept,penalty=penalty,alpha=x1,ncongrid=ncongrid)$gcv; f2<-rssfitM(xvals,yvals,nknots,Boundary.knots,intercept=intercept,penalty=penalty,alpha=x2,ncongrid=ncongrid)$gcv; while(abs(x3-x0)>tol*(abs(x1)+abs(x2))) { if(f2= 0 or <=0 according to whether signs[i] is +ive or -ive. # Uses solve.QP to apply sign constraints at the knots # x1<-x[,1]; x2<-x[,2]; signs<-signs/abs(signs); origin1<-Boundary.knots1[1]; fac1<-(Boundary.knots1[2]-Boundary.knots1[1])/(nknots1+1); knots1<-origin1+c(1:nknots1)*fac1; x1mat<-rssbasis(x1,knots1,origin1,intercept1); origin2<-Boundary.knots2[1]; fac2<-(Boundary.knots2[2]-Boundary.knots2[1])/(nknots2+1); knots2<-origin2+c(1:nknots2)*fac2; x2mat<-rssbasis(x2,knots2,origin2,intercept2); xmat<-x1mat%~%x2mat; XTX<-t(xmat)%*%xmat; d1<-rep(1,dim(x1mat)[2]); d1[1:3]<-0; if(intercept1) d1[4]<-0; d2<-rep(1,dim(x2mat)[2]); d2[1:3]<-0; if(intercept2) d2[4]<-0; D<-c((10^alpha[1])*d1,(10^alpha[2])*d2); D<-diag(D); # qprog matrices qmat<-XTX+D; rmat<-as.vector(t(xmat)%*%y); # constrain the sign of each term # aknots<-c(Boundary.knots1[1],knots1,Boundary.knots1[2]); cmat1<- signs[1]*rssbasis(aknots,knots1,origin1,intercept1); aknots<-c(Boundary.knots2[1],knots2,Boundary.knots2[2]); cmat2<- signs[2]*rssbasis(aknots,knots2,origin2,intercept2); cmat<-zeros(rows(cmat1)+rows(cmat2),cols(cmat1)+cols(cmat2)); cmat[1:rows(cmat1),1:cols(cmat1)]<-cmat1; cmat[(1+rows(cmat1)):rows(cmat),(1+cols(cmat1)):cols(cmat)]<-cmat2; qpfit<-solve.QP(Dmat=qmat,dvec=rmat,Amat=t(cmat),meq=0,factorized=F); betahat<-qpfit$solution; yhat<-xmat%*%betahat; yhat1<-x1mat%*%betahat[1:cols(x1mat)]; yhat2<-x2mat%*%betahat[(1+cols(x1mat)):(cols(x1mat)+cols(x2mat))]; msr<-mean((yhat-y)^2); iact<-qpfit$iact; nact<-sum(iact>0); if(nact<1) { hat<-xmat%*%solve(XTX+D,tol=1.e-16)%*%t(xmat); Z<-1; } else { iact<-iact[iact>0]; Cact<-cmat[iact,]; if(nact<2) Cact<-matrix(Cact,1,cols(cmat)) Z<-Null(t(Cact)); ZT<-t(Z); hat<-xmat%*%Z%*%solve(ZT%*%(XTX+D)%*%Z,tol=1.e-16)%*%ZT%*%t(xmat); } enp<-sum(diag(hat)); d1<-sum(diag(hat))/length(y); if(penalty*d1>1) {gcv<-1.0e12} else {gcv<-msr/((1-penalty*d1)^2)}; return(list(x=x,knots1=knots1,knots2=knots2,origin1=origin1, origin2=origin2, alpha=alpha, intercept1=intercept1, intercept2=intercept2,coef=betahat,fitted.values=yhat1%~%yhat2,gcv=gcv, penalty=penalty,enp=enp)) } gcvscore.rssadd2<-function(par,x,y,nknots1=20,nknots2=20,Boundary.knots1,Boundary.knots2, intercept1=T,intercept2=T,penalty=1,signs=c(1,1)) { fit<-rssadd2(x,y,nknots1=nknots1,nknots2=nknots2,Boundary.knots1=Boundary.knots1, Boundary.knots2=Boundary.knots2,intercept1=intercept1,intercept2=intercept2, penalty=penalty,alpha=par,signs=signs) return(log10(fit$gcv)) } gcv.rssadd2<-function(x,y,nknots1=20,nknots2=20,Boundary.knots1=NA,Boundary.knots2=NA, intercept1=T,intercept2=T,penalty=1,signs=c(1,1),par=c(0,0)) { if(is.na(Boundary.knots1)) Boundary.knots1<-range(x[,1]); if(is.na(Boundary.knots2)) Boundary.knots2<-range(x[,2]); bestfit<-optim(par,gcvscore.rssadd2,gr=NULL, method="Nelder-Mead", lower=-Inf, upper=+Inf, control=list(maxit=1000,trace=3), hessian=F, x,y,nknots1,nknots2,Boundary.knots1, Boundary.knots2,intercept1=intercept1,intercept2=intercept2,penalty,signs); hopt<-bestfit$par; bestfit<-optim(hopt,gcvscore.rssadd2,gr=NULL, method="Nelder-Mead", lower=-Inf, upper=+Inf, control=list(maxit=1000,trace=3), hessian=F, x,y,nknots1,nknots2,Boundary.knots1, Boundary.knots2,intercept1,intercept2,penalty,signs); hopt<-bestfit$par; rssfit<-rssadd2(x,y,nknots1,nknots2,Boundary.knots1,Boundary.knots2,intercept1=intercept1, intercept2=intercept2,penalty=penalty,alpha=bestfit$par,signs=signs); return(list(hopt=bestfit$par,gcv=rssfit$gcv, fitted.values=rssfit$fitted.values,model=rssfit)); } predict.rssadd2<-function(model, newx) { x1<-newx[,1]; x2<-newx[,2]; x1mat<-rssbasis(x1,model$knots1,model$origin1,model$intercept1); x2mat<-rssbasis(x2,model$knots2,model$origin2,model$intercept2); betahat<-model$coef; yhat1<-x1mat%*%betahat[1:cols(x1mat)]; yhat2<-x2mat%*%betahat[(1+cols(x1mat)):(cols(x1mat)+cols(x2mat))]; return(list(x=newx,y=yhat1%~%yhat2)); }