Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fittable 1D spline models #11634

Merged
merged 1 commit into from
Oct 30, 2021

Conversation

WilliamJamieson
Copy link
Contributor

@WilliamJamieson WilliamJamieson commented Apr 23, 2021

Description

This pull request introduces fittable spline models to ~astropy.modeling based on the scipy.interpolate spline models.

Note that this only introduces 1D spline models; 2D spline models will be introduced in a later pull request.

Note that this pull request depends on pull request #11548.

@github-actions
Copy link

👋 Thank you for your draft pull request! Do you know that you can use [ci skip] or [skip ci] in your commit messages to skip running continuous integration tests until you are ready?

@pllim
Copy link
Member

pllim commented Apr 23, 2021

I can't tell if you already put 43 commits into this PR or you have unrelated commits...

@WilliamJamieson
Copy link
Contributor Author

I can't tell if you already put 43 commits into this PR or you have unrelated commits...

The first 22 of them are from the PR #11548 merge. The rest are the development of this.

@WilliamJamieson WilliamJamieson force-pushed the tickets/al-511_merge_al-251 branch 2 times, most recently from 83a8709 to 465dc51 Compare April 27, 2021 17:27
@WilliamJamieson WilliamJamieson marked this pull request as ready for review April 27, 2021 17:27
@WilliamJamieson
Copy link
Contributor Author

I can't tell if you already put 43 commits into this PR or you have unrelated commits...

The first 22 of them are from the PR #11548 merge. The rest are the development of this.

It has now been rebased and squashed. So it should be way easier to review now!

astropy/modeling/spline.py Outdated Show resolved Hide resolved
astropy/modeling/spline.py Outdated Show resolved Hide resolved
astropy/modeling/spline.py Outdated Show resolved Hide resolved
@chris-simpson
Copy link
Contributor

The astropy models do not require that x be sorted into increasing order. For uniformity should that requirement be dropped here by sorting the input arrays before sending them to the scipy routine?

@WilliamJamieson
Copy link
Contributor Author

WilliamJamieson commented Apr 27, 2021

The astropy models do not require that x be sorted into increasing order. For uniformity should that requirement be dropped here by sorting the input arrays before sending them to the scipy routine?

@chris-simpson I think if I re-work this to use splrep as you suggested, then this requirement can be dropped. splrep's documentation does not indicate that the data needs to be sorted.

@WilliamJamieson
Copy link
Contributor Author

The astropy models do not require that x be sorted into increasing order. For uniformity should that requirement be dropped here by sorting the input arrays before sending them to the scipy routine?

@chris-simpson I think if I re-work this to use splrep as you suggested, then this requirement can be dropped. splrep's documentation does not indicate that the data needs to be sorted.

I amend this statement. It certainly implies that the data should be sorted. It assumes that the independent variables x have the condition x[0] <= x[j] <= x[-1] for all j by default. The notes on the "Schoenberg-Whitney conditions" also indicate that the x should be sorted.

Its a simple enough step to run a sort on x (and apply the results to y) as part of the fit before passing to splrep.

@jehturner
Copy link
Member

We also noticed a bit of work being done on spline models & fitters in astropy/specutils#291 (which has likewise attracted some input from STScI, though it was last updated a year ago), if you wanted to co-ordinate a bit? It would certainly be nice to get splines into modeling instead of all having our own implementations 👍.

@WilliamJamieson
Copy link
Contributor Author

We also noticed a bit of work being done on spline models & fitters in astropy/specutils#291 (which has likewise attracted some input from STScI, though it was last updated a year ago), if you wanted to co-ordinate a bit? It would certainly be nice to get splines into modeling instead of all having our own implementations 👍.

Sure, I would be happy to co-ordinate so that everyone gets what they need!

@pep8speaks
Copy link

pep8speaks commented Apr 30, 2021

Hello @WilliamJamieson 👋! It looks like you've made some changes in your pull request, so I've checked the code again for style.

There are no PEP8 style issues with this pull request - thanks! 🎉

Comment last updated at 2021-10-30 00:07:24 UTC

@WilliamJamieson WilliamJamieson force-pushed the tickets/al-511_merge_al-251 branch 3 times, most recently from d5990ec to dd2918d Compare April 30, 2021 19:06
@WilliamJamieson WilliamJamieson force-pushed the tickets/al-511_merge_al-251 branch 4 times, most recently from a34e5e6 to 2b97737 Compare August 9, 2021 18:48
@chris-simpson
Copy link
Contributor

May I suggest some tidying around the edges?

  1. Attempting to evaluate an unfitted spline:
>>> m = models.Spline1D()
>>> m(0)
WARNING: The fit degree have not been defined yet! [astropy.modeling.spline]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 295, in __call__
    return super().__call__(*args, **kwargs)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 397, in __call__
    __call__, args, kwargs, varargs='inputs', varkwargs='new_inputs')
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 375, in __call__
    return super(cls, self).__call__(*inputs, **kwargs)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 903, in __call__
    return generic_call(self, *new_args, **kwargs)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 4108, in generic_call
    outputs = self.evaluate(*chain(inputs, parameters))
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 273, in evaluate
    if kwargs['nu'] > self.degree + 1:
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

The warning isn't particularly useful if an exception gets raised immediately. I'm also not sure that the warning is worded correctly, since the "fit degree" isn't something the user ever defines. Splines obviously differ from polynomials because they can only be evaluated after the fitting is performed. If this hasn't happened then I think either a more specific exception should be raised, or the spline should evaluate to 0.

  1. I think the method parameter is unnecessary because you cannot specify both s and t; if both parameters are sent to splrep then s is ignored (task=-1) according to the documentation. Therefore one method (LSQUnivariateSpline, splrep.task=-1, or lsq) gets activated if t is specified, while the other (UnivariateSpline, splrep.task=0, or interpolate) gets activated if s is specified. If both are specified there should be an exception.

  2. Unlike the other Fitter classes, the SplineFitter appears only to accept arrays as inputs, not other sequences such as lists, and I think this should be remedied.

>>> x=[1,2,3,4]
>>> y=[1,2,3,4]
>>> f=fitting.SplineFitter()
>>> f(m, x, y, s=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/fitting.py", line 1664, in __call__
    raise ValueError('Least squares fitting requires a knots')
ValueError: Least squares fitting requires a knots
>>> f(m, x, y, s=0, method='interpolate')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/fitting.py", line 1675, in __call__
    fp, ier, msg = model_copy.interpolate_data(x, y, w=w, k=k, s=s, t=t)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 394, in interpolate_data
    x, y = self._sort_xy(x, y)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 343, in _sort_xy
    return x[arg_sort], y[arg_sort]
TypeError: only integer scalar arrays can be converted to a scalar index
>>> f(m, np.asarray(x), np.asarray(y), s=0, method='interpolate')
<Spline1D()>
  1. There seems to be an issue with the definition of knots. splrep only requires the internal knot(s) to be defined (if any), adding the knots at the end automatically, which is standard behaviour.
>>> x=np.arange(10)
>>> y=np.arange(10)
>>> splrep(x, y, t=(5,))
(array([0., 0., 0., 0., 5., 9., 9., 9., 9.]), array([-1.45336849e-16,  1.66666667e+00,  4.66666667e+00,  7.66666667e+00,
        9.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00]), 3)

but

>>> f(m, x, y, t=(5,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/fitting.py", line 1678, in __call__
    model_copy.fit_data(x, y, t, w=w, k=k, ghost_knots=ghost_knots)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 447, in fit_data
    check_finite=check_finite)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/scipy/interpolate/_bsplines.py", line 983, in make_lsq_spline
    if k > 0 and np.any((x < t[k]) | (x > t[-k])):
IndexError: index 3 is out of bounds for axis 0 with size 1

Maybe I'm doing something wrong here?

@WilliamJamieson WilliamJamieson marked this pull request as draft August 9, 2021 22:49
@WilliamJamieson
Copy link
Contributor Author

May I suggest some tidying around the edges?

  1. Attempting to evaluate an unfitted spline:
>>> m = models.Spline1D()
>>> m(0)
WARNING: The fit degree have not been defined yet! [astropy.modeling.spline]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 295, in __call__
    return super().__call__(*args, **kwargs)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 397, in __call__
    __call__, args, kwargs, varargs='inputs', varkwargs='new_inputs')
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 375, in __call__
    return super(cls, self).__call__(*inputs, **kwargs)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 903, in __call__
    return generic_call(self, *new_args, **kwargs)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/core.py", line 4108, in generic_call
    outputs = self.evaluate(*chain(inputs, parameters))
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 273, in evaluate
    if kwargs['nu'] > self.degree + 1:
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

The warning isn't particularly useful if an exception gets raised immediately. I'm also not sure that the warning is worded correctly, since the "fit degree" isn't something the user ever defines.

Agreed, the warning being raised is intended for a different case (user accessing spline information prior to a fit) then what it is being raised for here. Instead it is probably better for it to raise a proper error instead.

Splines obviously differ from polynomials because they can only be evaluated after the fitting is performed. If this hasn't happened then I think either a more specific exception should be raised, or the spline should evaluate to 0.

This seems reasonable, although I think it would be better to have the spline simply raise a much more useful error as it could be misleading. I am open to debate about this.

  1. I think the method parameter is unnecessary because you cannot specify both s and t; if both parameters are sent to splrep then s is ignored (task=-1) according to the documentation. Therefore one method (LSQUnivariateSpline, splrep.task=-1, or lsq) gets activated if t is specified, while the other (UnivariateSpline, splrep.task=0, or interpolate) gets activated if s is specified. If both are specified there should be an exception.

The method parameter is necessary because s is the smoothing factor applied to the interpolation during an interpolation fit. When it is not defined, s is chosen automatically by scipy. During interpolation, when t is specified s has to be ignored because it removes the the freedom of the interpolation algorithm to choose knots so that the desired smoothing factor can be achieved.

This points me to the conclusion that interpolation and lsq spline fitting should be separated into distinct fitters. I debated whether or not to combine the fitters (as I have done currently). This is because the code was originally written to just perform interpolation fitting, while a least squares fitting method was recently requested.

  1. Unlike the other Fitter classes, the SplineFitter appears only to accept arrays as inputs, not other sequences such as lists, and I think this should be remedied.
>>> x=[1,2,3,4]
>>> y=[1,2,3,4]
>>> f=fitting.SplineFitter()
>>> f(m, x, y, s=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/fitting.py", line 1664, in __call__
    raise ValueError('Least squares fitting requires a knots')
ValueError: Least squares fitting requires a knots
>>> f(m, x, y, s=0, method='interpolate')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/fitting.py", line 1675, in __call__
    fp, ier, msg = model_copy.interpolate_data(x, y, w=w, k=k, s=s, t=t)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 394, in interpolate_data
    x, y = self._sort_xy(x, y)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 343, in _sort_xy
    return x[arg_sort], y[arg_sort]
TypeError: only integer scalar arrays can be converted to a scalar index
>>> f(m, np.asarray(x), np.asarray(y), s=0, method='interpolate')
<Spline1D()>

Agreed, I overlooked testing for this. This should be an easy fix.

  1. There seems to be an issue with the definition of knots. splrep only requires the internal knot(s) to be defined (if any), adding the knots at the end automatically, which is standard behaviour.
>>> x=np.arange(10)
>>> y=np.arange(10)
>>> splrep(x, y, t=(5,))
(array([0., 0., 0., 0., 5., 9., 9., 9., 9.]), array([-1.45336849e-16,  1.66666667e+00,  4.66666667e+00,  7.66666667e+00,
        9.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00]), 3)

but

>>> f(m, x, y, t=(5,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/fitting.py", line 1678, in __call__
    model_copy.fit_data(x, y, t, w=w, k=k, ghost_knots=ghost_knots)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/astropy/modeling/spline.py", line 447, in fit_data
    check_finite=check_finite)
  File "/opt/anaconda3/envs/unstable/lib/python3.7/site-packages/scipy/interpolate/_bsplines.py", line 983, in make_lsq_spline
    if k > 0 and np.any((x < t[k]) | (x > t[-k])):
IndexError: index 3 is out of bounds for axis 0 with size 1

Maybe I'm doing something wrong here?

This is a product of combining the interpolation and least squares fitting into a single interface. Essentially, what is happening is that least squares fitting is using make_lsq_spline, which expects the user pass knots which include the "ghost"/"degenerate" knots at either end of the knot list (these are needed for the least squares spline fitting algorithm). That is it expects k+1 knots matching the lower bound at the start and k+1 knots matching the upper bound at the end. This is different than what splrep expects (there is at least one data point between each successive knot) .

Note that splrep can be very finicky about user defined knots in my experience. Under most of the realistic cases that I have tried to define my own knots of splrep it will return a fitting error and fail. This is part of the reason why I introduced the least-squares fitter, as it is very robust under user defined knots.

I think the conclusion is that I should create a SplineInteroplateFitter and a SplineLSQFitter.

@chris-simpson
Copy link
Contributor

I'm confused by your nomenclature, and also by your trouble with knots. Let me set up some data:

>>> x=np.arange(100)
>>> y = x*x
>>> t = (10,20,30,40,50,60)

Here are three ways to make a least-squares spline:

>>> s = LSQUnivariateSpline(x, y, t=(10,20,30,40,50,60))
>>> s.get_knots() # returns t, without duplicating the boundary knots
array([ 0., 10., 20., 30., 40., 50., 60., 99.])
>>> s.get_coeffs(). # returns c
array([-1.03874398e-13,  3.73486535e-13,  6.66666667e+01,  3.66666667e+02,
        8.66666667e+02,  1.56666667e+03,  2.46666667e+03,  4.63000000e+03,
        7.22700000e+03,  9.80100000e+03])
>>> splrep(x, y, t=t, task=-1) # returns t, c, k to make a BSpline
(array([ 0.,  0.,  0.,  0., 10., 20., 30., 40., 50., 60., 99., 99., 99.,
       99.]), array([-1.03874398e-13,  3.73486535e-13,  6.66666667e+01,  3.66666667e+02,
        8.66666667e+02,  1.56666667e+03,  2.46666667e+03,  4.63000000e+03,
        7.22700000e+03,  9.80100000e+03,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00]), 3)
>>> s=make_lsq_spline(x, y,  t=(0,)*4+t+(99,)*4) # returns a BSpline
>>> s.tck
(array([ 0.,  0.,  0.,  0., 10., 20., 30., 40., 50., 60., 99., 99., 99.,
       99.]), array([ 2.82576249e-13, -9.89850226e-13,  6.66666667e+01,  3.66666667e+02,
        8.66666667e+02,  1.56666667e+03,  2.46666667e+03,  4.63000000e+03,
        7.22700000e+03,  9.80100000e+03]), 3)

and they're all the same (there are negligible floating-point differences between the first two and the third).

For make_lsq_spline I've had to add in the k+1 boundary knots (which you call "ghost" knots) at x[0] and x[-1] but you note that the documented example for make_lsq_spline() does the same. This is normal practice so defining a cubic spline with no internal knots and only the boundary knots returns the same function as fitting a cubic polynomial (as you would expect). I'm not sure what benefit you gain by deviating from that.

This is one type of spline, where the user specifies t but not s. For the other type, scipy.interpolate.UnivariateSpline or splrep(task =0), the user specifies s but not t. Hence my statement that there are only two types of spline that can be identified by whether the user specifies s or t, and you can just change the parameters of the call to splrep (so having two different classes would produce unnecessary code duplication). You seem to be suggesting that splrep cannot do what you need it to (and therefore you have significantly different code for the different types, making two separate classes more sensible) because of problems with the knots, but I'm not sure what you mean.

@WilliamJamieson
Copy link
Contributor Author

I'm confused by your nomenclature, and also by your trouble with knots.

I should have been more clear when it came to the knots. The original desired application, required an "interpolating" spline of the data using a predefined set of knots (generated by an independent calculation, which I was not provided). Note that interpolation requires that the for a data point (x_j, y_j) that the function f has the property f(x_j) = y_j, that is the interpolating function must exactly match all the data points (this is the mathematical definition). The "interpolation" requirement was why the spline library was written to focus on allowing one to fit better and better interpolating splines. Basically, applying task=0. It turns out that when we received simulated data splrep returns an error (a very unhelpful error at that) when you use the provided knots alongside the data and any "good" s value. As it turns out the application really wanted one to fit a spline through a set of pre-defined knots to a set of data, not refine an existing interpolation. Hence the quick pivot to least squares fitting.

I totally missed the task=-1 option in splrep and focused on the make_lsq_spline because the knots the application wanted the fit to work on included the "ghost" knots and so no major alterations needed to be made. Thanks for pointing these issues (this is very much a work in progress). I can consolidate the interface back down to a single fitter and scipy function for both 1D and 2D splines.

@WilliamJamieson WilliamJamieson marked this pull request as ready for review August 10, 2021 16:34
@WilliamJamieson
Copy link
Contributor Author

After offline discussion with @nden and @perrygreenfield, we decided to move the spline fitters to spline.py and remove the metaclass from them.

@nden
Copy link
Contributor

nden commented Oct 29, 2021

@chris-simpson @jehturner DO you have any other comments on this?

@jehturner
Copy link
Member

I haven't been keeping up very well but I'll pull this and kick it around very quickly. Thanks for working on it. If everything looks good I imagine we'll replace our spline fitting in fit_1D with this at some point. We do have several tests for that code, which should help to confirm that the results are as expected.

@nden
Copy link
Contributor

nden commented Oct 29, 2021

One change I'd suggest is removing the classmethods and moving the instance methods under the fitters. For example, move Spline.interpolated_dataunderSplineINterpolatedFitterand removeSpline.interpolate`. In this way there will be only one way to the operation as opposed to three ways doing the same things.

Also the degree setter should probably be removed.

@eteq
Copy link
Member

eteq commented Oct 29, 2021

Hmm. I see @nden's point that there are too many different ways to do the fitting. However, I'm not sure if I like the suggestion above because I was thinking :

  1. most users think about the model first and the fitter second or never, so I wanted to expose the concept of the different "kinds" of spline fitting at the model level,
  2. these are already different from the previous model+fitter paradigm because the model really only works with particular fitters, so being a little different is ok since they're already a little different

So maybe that argues for removing Spline.interpolate but leaving interpolated_data in the model. It's not completely consistent with how the other fitters work, but when I try to think of myself as a user, it feels a lot more convenient to use that way.

Another option is to do the most idiomatic of the three ways (which I think is @nden's suggestion above?) now, but have a follow-on PR for 5.1 that adds the above back in, since that avoids the problem of API lock-in.

@WilliamJamieson
Copy link
Contributor Author

I think the current CI failures in this PR are from #12324, not this PR itself.

@jehturner
Copy link
Member

I'm not spotting any mention of splines in the narrative docs -- just an automodapi reference. Does some description still need adding there?

@nden
Copy link
Contributor

nden commented Oct 29, 2021

The narrative docs will be updated next week. The feature freeze today is only for new features.

@nden
Copy link
Contributor

nden commented Oct 29, 2021

This is ready to be merged after the unrelated CI failures are resolved.

@jehturner
Copy link
Member

OK. It will be nice to see a summary/comparison of the different kinds of fitters in particular.

I haven't pulled AstroPy for a while and ran into some problems with dependency versions, in between meetings etc. Superficially I would say this looks very promising though (I will look at it more when I can). Do the fitters handle model sets with masked data nicely? Has this been tested with FittingWithOutlierRemoval? Those things are rather important, but I'm not seeing references to mask in the code. Fair enough if that's a work in progress and the code does something useful in the meantime; it's hard to see what would prevent adding such functionality later. Thanks!

@WilliamJamieson
Copy link
Contributor Author

Has this been tested with FittingWithOutlierRemoval?

No. However, a very similar iterative rejection fitting method (its not ready for merging into astropy yet) has been partially tested with these splines. So I feel like it should be possible to get it to work with FittingWithOutlierRemoval fairly easily in the future.

Added initial spline

More additions to spline

Added spline model evaluate

Filled in warnings

Added reset for spline.

Added bounding box error fix and evaluate derivative passthrough

Added SplineFitter classes

Added Spline2D support

Fixes to some bugs in spline2D

Setting up for tests

Added fitting tests and fixed bounding box

Added tests for optional argument hack

Added warning tests for spline fitter

Initial tests for Spline2D

Initial fitter tests for Spline2D

Finished adding Spline2D fit tests

Added call test for Spline2D hack

Added SplineFitter tests

Added SplineLSQFitter

Added some documentation.

Refactored common code in bbox handling.

Refactored to allow access to underlying spline

Moved bounding box into abstract sub-class

Abstracted optional input handling

Made optional input parameters private variables

Fully tested abstract base class

Added changelog fragment.

Added splines to common name spaces

Fixed circular import error.

Initial tck-refactor work

Partial refactor to using tck instead of spline class in 1D

Almost completed Spline1D refactor

Refactored 1D spline is fully tested now

Start of refactor of _Spline tests

Added _Spline tests

Refactored so that dimension was better accounted for in _Spline

Spline2D transition to using tck tuple done except for fitting.

Added Spline2D fitter

Cleaned up comments

Added Fitter for Spline1D

Added 2D spline fitter

Fixed codestyle errors

Added updated the basic documentation.

Added input sorting

Fixed code style and pep8speaks warning (used walrus operator which is not supported in python 3.7)

Fixed more walrus errors

Fixed scipy testing error

Removed scipy from _Spline test

Fixed code coverage and added better tck protection

Added derivative and antiderivative constructors for Spline1D

Fixed test broken by rebase

Made unfit spline default to 0 as requested

Updates to insure 1D spline fitting can accept non-numpy array data.

Added full out lsq fitter tests.

Increase code coverage for spline.

Import new 1D spline API

Removing unnecessary changes to `modeling/utils.py`

Added tests for uncovered lines

Fixing code documentation

Fixed missing docstrings and typos in docstrings.

Added example to spline

Changed fitting interface to use `weights` keyword instead of `w`.

Added whatsnew entry for splines

Update docs/whatsnew/5.0.rst

Co-authored-by: Erik Tollerud <erik.tollerud@gmail.com>
Incorporated eteq's suggestions for changing docstrings

At eteq's suggestion moved from using lsq to using term exact_knots

Moved spline fitters from `fitting.py` to `spline.py`

Fixed doctest fail

Copied spline fitting code into fitter.

Removed bound constructors

Removed interpolate_data method

Removed `smoothing_fit` method

Removed `exact_knots_fit`

Removed `sprep_data` method

Removed degree setter.

Updated doc-string to reflect new interfaces.
@nden nden merged commit 199b3dd into astropy:main Oct 30, 2021
@WilliamJamieson WilliamJamieson deleted the tickets/al-511_merge_al-251 branch October 30, 2021 01:53
@astrofrog
Copy link
Member

This seems like a nice new feature to highlight in the 'What's new in 5.0?' page.

@WilliamJamieson - could you consider opening a pull request to the v5.0.x branch to add an entry to this file:

https://github.com/astropy/astropy/blob/v5.0.x/docs/whatsnew/5.0.rst

to advertise this new feature? (including a plot if possible!)

@astrofrog
Copy link
Member

I see there is already an entry for this but adding a plot showing an example of a fitted spline would be a nice addition

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants