I have the following data to which I want to fit a monotone non-decreasing spline.

x <- c(300, 377, 455, 533, 611, 688, 766, 844, 922, 1000)  
y <- c(0.5, 0.8, 1, 1, 1, 1, 1, 1, 1, 1)

To achieve this, I use an I-Spline basis (i.e., splines2::iSpline) paired with non-negative coefficients (i.e., nnls::nnls).

# Inner knots.
k <- quantile(x, probs = c(.25, .5, .75))

h <- splines2::iSpline(x, knots = k, Boundary.knots = range(x), intercept = TRUE)
a <- nnls::nnls(h, y)$x

And this gives me the following:

# Fitted.
f <- h %*% a

#             [,1]
#   [1,] 0.0000000
#   [2,] 0.8106201
#   [3,] 0.9699174
#   [4,] 0.9992354
#   [5,] 1.0035000
#   [6,] 1.0035515
#   [7,] 1.0035515
#   [8,] 1.0035515
#   [9,] 1.0035515
#  [10,] 1.0035515

However, with intercept = TRUE, I would expect the spline to start somewhere above the first data point. For instance, this seems to be the case when I set the first basis function to 1.

h[, 1] <- 1

If I set intercept = FALSE, then things go haywire.


Can you please help me understand what is going on, i.e., why is the spline not starting at (300, 0.5)?

Further Details

I want to clarify my question further. My confusion stems from what is written in this paper on p. 15:

To get a basis for the increasing splines we need to add the constant function to the I-splines and allow it to enter the linear combination with either sign.

In the splines2 package documentation, where the intercept argument for iSpline() is described, it says:

Notice that when using I-Spline for monotonic regression, intercept = TRUE should be set even when an intercept term is considered additional to the spline bases in the model.

What I get from this is that if I want to fit a monotone spline with, say, nnls::nnls(), then I need to add a constant function to the I-Spline basis, i.e., h <- cbind(1, h), assuming that h is the basis matrix output of iSpline().

However, in the paper linked above, that's not always the case. Sometimes the author replaces the first column of the basis matrix (i.e., h <- cbind(1, h[ -1]) with the constant function, and, other times, he appends the constant function to the basis matrix.

For instance, if I create the I-Spline basis matrix as follows:

h <- splines2::iSpline(x, knots = c(3, 5, 8), degree = 0, intercept = TRUE)

Then, depending on how I "add" the constant function (or not) I get different splines:


What's not clear to me is how the basis matrix provided by iSpline() is intended to be used in this case.


From the definition of k in your code we know the first segment is fitting more than 2 data points.

Only if it is fitting first 2 data points and without the constraint that the function to be continuous, the spline will pass the first point. Note, if that is the case, the model is not a spline.

x <- c(300, 377, 455, 533, 611, 688, 766, 844, 922, 1000)  
y <- c(0.5, 0.8, 1, 1, 1, 1, 1, 1, 1, 1)
group <- c(0,0,1, 1, 1, 1, 1, 1, 1, 1)
d = data.frame(x=x,y=y, group=group)
ggplot(d,aes(x,y, group=group))+geom_point()+geom_smooth(method='lm')+theme_gray(20)

enter image description here

  • $\begingroup$ Haitao Du, what you wrote makes sense. However, I am still confused because in this paper on p. 15 (last sentence), it says that a constant function needs to be added to the I-Spline basis matrix. However, in the paper, the first column of the basis matrix is sometimes replaced with ones (i.e., h <- cbind(1, h[ -1]) and other times simply another column is appended (i.e., h <- cbind(1, h)). Can you please shed some light on why this is the case? $\endgroup$ – Mihai Apr 20 at 8:59
  • $\begingroup$ I will append this to the question above. $\endgroup$ – Mihai Apr 20 at 9:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.