In the documentation for mgcv::pcls()
, an example of constructing a monotonically increasing gam model is provided with the following code:
set.seed(1234) # added for reproducibility in this post only
x <- runif(100)*4-1;x <- sort(x);
f <- exp(4*x)/(1+exp(4*x)); y <- f+rnorm(100)*0.1; plot(x,y)
dat <- data.frame(x=x,y=y)
# Show regular spline fit (and save fitted object)
f.ug <- gam(y~s(x,k=10,bs="cr")); lines(x,fitted(f.ug))
# Create Design matrix, constraints etc. for monotonic spline....
sm <- smoothCon(s(x,k=10,bs="cr"),dat,knots=NULL)[[1]]
F <- mono.con(sm$xp); # get constraints
G <- list(X=sm$X,C=matrix(0,0,0),sp=f.ug$sp,p=sm$xp,y=y,w=y*0+1)
G$Ain <- F$A;G$bin <- F$b;G$S <- sm$S;G$off <- 0
p <- pcls(G); # fit spline (using s.p. from unconstrained fit)
fv<-Predict.matrix(sm,data.frame(x=x))%*%p
lines(x,fv,col=2)
This produces the follow plot (if set.seed(1234)
is used):
How does one correctly constrain the monotonically increasing model so that the lower bound is 0.1 and the upper bound is 1.0? I naively initially thought one could set these values as the lower
and upper
bound values in mono.con
. For example F<-mono.com(sm$xp, lower=0.1, upper=1.0)
, but this will return the following value for F$b
:
[1] 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
[25] 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 -1.0
(i.e. lower
and -1*upper
have been appended to F$b
(and dim(F$A)[1]
has similarly increased by 2), but the call to pcls(G)
returns the following:
Error in pcls(G) : initial parameters not feasible
Is there a modification to example code in ?pcls
that can constrain the monototonically increasing gam to between the blue lines in this image below:
Here is the relevant code:
F<-mono.con(sm$xp, lower=0.1,upper=1)
F$A[4*(10-1)+1,1] <- 1
G <- list(X=sm$X,C=matrix(0,0,0),sp=f.ug$sp,p=scales::rescale(sm$xp,to=c(0.4,0.7)),y=y,w=y*0+1)
G$Ain <- F$A;G$bin <- F$b;G$S <- sm$S;G$off <- 0
p <- pcls(G)
> p
[1] 0.1000000 0.1346833 0.2752035 0.5562702 0.7425019 0.9762224 0.9891460 1.0000000 1.0000000 1.0000000
Few notes:
The error message mentions initial parameters not feasible. Therefore, we can rescale the initial parameter (G$p
) to satisfy the constraint.
There is a bug when constructing inequality matrix in mono.con()
as the lower bound constraint becomes [0 0 0 0 0...]x >= 0.1 instead of [1 0 0 0 0]x >= 0.1. I think the issues is this where 4*n
should be 4*n+lo
. This is why I temporarily add F$A[4*(10-1)+1,1] <- 1
.
For decreasing constraint, we would do the following:
F<-mono.con(sm$xp,up=FALSE, lower=0.1,upper=1)
F$A[4*(10-1)+1,10] <- 1
F<-mono.con(sm$xp,up=FALSE)
G <- list(X=sm$X,C=matrix(0,0,0),sp=f.ug$sp,p=scales::rescale(sm$xp,to=c(0.7,0.4)),y=y,w=y*0+1)
G$Ain <- F$A;G$bin <- F$b;G$S <- sm$S;G$off <- 0
p <- pcls(G); # fit spline (using s.p. from unconstrained fit)
p
[1] 1.0000000 1.0000000 1.0000000 0.9891460 0.9762224 0.7425019 0.5562702 0.2752035 0.1346833 0.1000000
Namely, sm$xp
is rescaled to c(0.7,0.4) and the lower bound constraint becomes [0 0 ... 0 0 1]x>=0.1 instead of [0 0 ... 0 0 0]x>=1.