Search code examples
rsmoothingkernel-density

How to fit a smooth line to some points but preserve monotonicity


I have the following data points

example<-structure(list(y = c(1, 0.961538461538462, 0.923076923076923, 
0.884615384615385, 0.846153846153846, 0.807692307692308, 0.769230769230769, 0.730769230769231, 0.730769230769231, 0.730769230769231, 0.687782805429864, 0.687782805429864, 0.641930618401207, 0.596078431372549, 0.596078431372549, 0.54640522875817, 0.496732026143791, 0.496732026143791, 0.496732026143791, 
0.496732026143791, 0.496732026143791, 0.496732026143791, 0.496732026143791, 0.496732026143791, 0.496732026143791, 0.496732026143791, 0.496732026143791
), x = c(0, 59, 115, 156, 268, 329, 353, 365, 377, 421, 431, 
448, 464, 475, 477, 563, 638, 744, 769, 770, 803, 855, 1040, 
1106, 1129, 1206, 1227)), .Names = c("y", "x"), row.names = c(NA, 
-27L), class = "data.frame")

I would like to fit a smooth line. There are several methods in R to do it, using loess, ksmooth, locpoly etc.

Is there any way however to ensure or force that the resulting smoothed line will be monotonic (in the case of the present example monotonically decreasing?)


Solution

  • You can use the scam() function in the scam package for uni- or multivariate smoothing with constraints. The help file, ?scam:::shape.constrained.smooth.terms shows all of the available options. For example, the B-spline basis which is used for smoothing can be penalized to yield monotonically decreasing coefficients with scam(y~s(x,bs="mpd")).

    require(scam)   
    
    attach(example)
    
    yhat <- predict(scam(y~s(x,bs="mpd")),se=TRUE)
    
    plot(x,y)
    lines(x,y=yhat$fit)
    lines(x,y=yhat$fit+1.96*yhat$se.fit,lty=2)
    lines(x,y=yhat$fit-1.96*yhat$se.fit,lty=2)