Prakhar Mehrotra
Prakhar Mehrotra

Reputation: 1231

Constrained Spline Fit using Scipy in Python

I was wondering if UnivariateSpline lets you do constrain spline fitting? For example, consider the following data:

 x         y
13    2.404070
12    1.588134
11    1.760112
10    1.771360
09    1.860087
08    1.955789
07    1.910408
06    1.655911
05    1.778952
04    2.624719
03    1.698099
02    3.022607
01    3.303135    

The discussion on the choice and role of smoothing function can be found in an earlier post here. However, if we impose a constrain that spline needs to be monotonically decreasing, any ideas on how should we go about fitting the spline?

Thanks for your help!.

EDIT: The spline need not exactly fit all the points. It should however satisfy the constrain (monotonically decreasing).

Upvotes: 5

Views: 6488

Answers (3)

Tom Wenseleers
Tom Wenseleers

Reputation: 7989

The pyGAM package can fit shape constrained splines, including monotonically increasing ones, see https://pygam.readthedocs.io/en/latest/notebooks/tour_of_pygam.html. UnivariateSpline cannot do this. In R there is plenty of packages that can do that, e.g. the scam package (which uses squared error loss) or the cobs package, which uses L1 loss.

Upvotes: 0

kalu
kalu

Reputation: 2682

A monotonicity constraint can be imposed by using integrated splines as basis functions and constraining the OLS coefficients to be non-negative. The non-negativity constraint makes computing a solution more difficult than fitting an ordinary smoothing spline. It is likely your scipy package does not have the necessary functionality.

Mary Meyer suggests using some interesting properties of the parameter space in this problem to facilitate computation. She provides R code in her paper. See Meyer (2008, AoAS), Inference using shape-restricted regression splines, at 1031.

Upvotes: 2

Dan Allan
Dan Allan

Reputation: 35235

  • Reading the question you linked, I think you only need x to be monotonic. If your data is a Series with x as the index, then just do UnivariateSpline(s.sort()). If your data is a DataFrame, do UnivariateSpline(df.set_index('x')['y'].sort()).

  • Perhaps you actually want a monotonic spline, in spite of the fact that y(x) does not seem to be monotonic. I know of no way to introduce constraints to UnivariateSpline directly, but we can constrain the data before we fit the spline. Generate a "forced monotonically decreasing" variant of your data like this:

    pd.expanding_min(s.sort())
    

    Each element will be replaced with the smallest element seen so far, suppressing any increases. Any spline from such data should also be monotonic.

  • Finally, in general, for curve fitting with constraints, checkout lmfit. It adds some features onto the nonlinear least-squared curve fitter scipy.optimize, and it's saved me a lot of hassle.

Upvotes: 1

Related Questions