~ubuntu-branches/ubuntu/karmic/python-scipy/karmic

« back to all changes in this revision

Viewing changes to Lib/interpolate/fitpack2.py

  • Committer: Bazaar Package Importer
  • Author(s): Daniel T. Chen (new)
  • Date: 2005-03-16 02:15:29 UTC
  • Revision ID: james.westby@ubuntu.com-20050316021529-xrjlowsejs0cijig
Tags: upstream-0.3.2
ImportĀ upstreamĀ versionĀ 0.3.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
fitpack --- curve and surface fitting with splines
 
3
 
 
4
fitpack is based on a collection of Fortran routines DIERCKX
 
5
by P. Dierckx (see http://www.netlib.org/dierckx/) transformed
 
6
to double routines by Pearu Peterson.
 
7
"""
 
8
# Created by Pearu Peterson, June,August 2003
 
9
 
 
10
__all__ = [
 
11
    'UnivariateSpline',
 
12
    'InterpolatedUnivariateSpline',
 
13
    'LSQUnivariateSpline',
 
14
 
 
15
    'LSQBivariateSpline',
 
16
    'SmoothBivariateSpline']
 
17
 
 
18
import warnings
 
19
from scipy_base import zeros, Float, concatenate, alltrue
 
20
from scipy_distutils.misc_util import PostponedException
 
21
 
 
22
try: import dfitpack
 
23
except: dfitpack = PostponedException()
 
24
 
 
25
################ Univariate spline ####################
 
26
 
 
27
_curfit_messages = {1:"""
 
28
The required storage space exceeds the available storage space, as
 
29
specified by the parameter nest: nest too small. If nest is already
 
30
large (say nest > m/2), it may also indicate that s is too small.
 
31
The approximation returned is the weighted least-squares spline
 
32
according to the knots t[0],t[1],...,t[n-1]. (n=nest) the parameter fp
 
33
gives the corresponding weighted sum of squared residuals (fp>s).
 
34
""",
 
35
                    2:"""
 
36
A theoretically impossible result was found during the iteration
 
37
proces for finding a smoothing spline with fp = s: s too small.
 
38
There is an approximation returned but the corresponding weighted sum
 
39
of squared residuals does not satisfy the condition abs(fp-s)/s < tol.""",
 
40
                    3:"""
 
41
The maximal number of iterations maxit (set to 20 by the program)
 
42
allowed for finding a smoothing spline with fp=s has been reached: s
 
43
too small.
 
44
There is an approximation returned but the corresponding weighted sum
 
45
of squared residuals does not satisfy the condition abs(fp-s)/s < tol.""",
 
46
                    10:"""
 
47
Error on entry, no approximation returned. The following conditions
 
48
must hold:
 
49
xb<=x[0]<x[1]<...<x[m-1]<=xe, w[i]>0, i=0..m-1
 
50
if iopt=-1:
 
51
  xb<t[k+1]<t[k+2]<...<t[n-k-2]<xe"""
 
52
                    }
 
53
 
 
54
class UnivariateSpline:
 
55
    """ Univariate spline s(x) of degree k on the interval
 
56
    [xb,xe] calculated from a given set of data points
 
57
    (x,y).
 
58
    """
 
59
 
 
60
    def __init__(self, x, y, w=None, bbox = [None]*2, k=3, s=None):
 
61
        """
 
62
        Input:
 
63
          x,y   - 1-d sequences of data points (x must be
 
64
                  in strictly ascending order)
 
65
 
 
66
        Optional input:
 
67
          w          - positive 1-d sequence of weights
 
68
          bbox       - 2-sequence specifying the boundary of
 
69
                       the approximation interval.
 
70
                       By default, bbox=[x[0],x[-1]]
 
71
          k=3        - degree of the univariate spline.
 
72
          s          - positive smoothing factor defined for
 
73
                       estimation condition:
 
74
                         sum((w[i]*(y[i]-s(x[i])))**2) <= s
 
75
                       Default s=len(w) which should be a good value
 
76
                       if 1/w[i] is an estimate of the standard
 
77
                       deviation of y[i].
 
78
        """
 
79
        #_data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier
 
80
        data = dfitpack.fpcurf0(x,y,k,w=w,
 
81
                                xb=bbox[0],xe=bbox[1],s=s)
 
82
        if data[-1]==1:
 
83
            # nest too small, setting to maximum bound
 
84
            data = self._reset_nest(data)
 
85
        self._data = data
 
86
        self._reset_class()
 
87
 
 
88
    def _reset_class(self):
 
89
        data = self._data
 
90
        n,t,c,k,ier = data[7],data[8],data[9],data[5],data[-1]
 
91
        self._eval_args = t[:n],c[:n-k-1],k
 
92
        if ier==0:
 
93
            # the spline returned has a residual sum of squares fp
 
94
            # such that abs(fp-s)/s <= tol with tol a relative
 
95
            # tolerance set to 0.001 by the program
 
96
            pass
 
97
        elif ier==-1:
 
98
            # the spline returned is an interpolating spline
 
99
            self.__class__ = InterpolatedUnivariateSpline
 
100
        elif ier==-2:
 
101
            # the spline returned is the weighted least-squares
 
102
            # polynomial of degree k. In this extreme case fp gives
 
103
            # the upper bound fp0 for the smoothing factor s.
 
104
            self.__class__ = LSQUnivariateSpline
 
105
        else:
 
106
            # error
 
107
            if ier==1:
 
108
                self.__class__ = LSQUnivariateSpline
 
109
            message = _curfit_messages.get(ier,'ier=%s' % (ier))
 
110
            warnings.warn(message)
 
111
 
 
112
    def _reset_nest(self, data, nest=None):
 
113
        n = data[10]
 
114
        if nest is None:
 
115
            k,m = data[5],len(data[0])
 
116
            nest = m+k+1 # this is the maximum bound for nest
 
117
        else:
 
118
            assert n<=nest,"nest can only be increased"
 
119
        t,c,fpint,nrdata = data[8].copy(),data[9].copy(),\
 
120
                           data[11].copy(),data[12].copy()
 
121
        t.resize(nest)
 
122
        c.resize(nest)
 
123
        fpint.resize(nest)
 
124
        nrdata.resize(nest)
 
125
        args = data[:8] + (t,c,n,fpint,nrdata,data[13])
 
126
        data = dfitpack.fpcurf1(*args)
 
127
        return data
 
128
 
 
129
    def set_smoothing_factor(self, s):
 
130
        """ Continue spline computation with the given smoothing
 
131
        factor s and with the knots found at the last call.
 
132
        """
 
133
        data = self._data
 
134
        if data[6]==-1:
 
135
            warnings.warn('smoothing factor unchanged for'
 
136
                          'LSQ spline with fixed knots')
 
137
            return
 
138
        args = data[:6] + (s,) + data[7:]
 
139
        data = dfitpack.fpcurf1(*args)
 
140
        if data[-1]==1:
 
141
            # nest too small, setting to maximum bound
 
142
            data = self._reset_nest(data)
 
143
        self._data = data
 
144
        self._reset_class()
 
145
 
 
146
    def __call__(self, x, nu=None):
 
147
        """ Evaluate spline (or its nu-th derivative) at positions x.
 
148
        Note: x can be unordered but the evaluation is
 
149
        more efficient if x is (partially) ordered.
 
150
        """
 
151
        if nu is None:
 
152
            return dfitpack.splev(*(self._eval_args+(x,)))
 
153
        return dfitpack.splder(nu=nu,*(self._eval_args+(x,)))
 
154
 
 
155
    def get_knots(self):
 
156
        """ Return the positions of (boundary and interior)
 
157
        knots of the spline.
 
158
        """
 
159
        data = self._data
 
160
        k,n = data[5],data[7]
 
161
        return data[8][k:n-k]
 
162
 
 
163
    def get_coeffs(self):
 
164
        """ Return spline coefficients."""
 
165
        data = self._data
 
166
        k,n = data[5],data[7]
 
167
        return data[9][:n-k-1]
 
168
 
 
169
    def get_residual(self):
 
170
        """ Return weighted sum of squared residuals of the spline
 
171
        approximation: sum ((w[i]*(y[i]-s(x[i])))**2)
 
172
        """
 
173
        return self._data[10]
 
174
 
 
175
    def integral(self, a, b):
 
176
        """ Return definite integral of the spline between two
 
177
        given points.
 
178
        """
 
179
        return dfitpack.splint(*(self._eval_args+(a,b)))
 
180
 
 
181
    def derivatives(self, x):
 
182
        """ Return all derivatives of the spline at the point x."""
 
183
        d,ier = dfitpack.spalde(*(self._eval_args+(x,)))
 
184
        assert ier==0,`ier`
 
185
        return d
 
186
 
 
187
    def roots(self):
 
188
        """ Return the zeros of the spline.
 
189
 
 
190
        Restriction: only cubic splines are supported by fitpack.
 
191
        """
 
192
        k = self._data[5]
 
193
        if k==3:
 
194
            z,m,ier = dfitpack.sproot(*self._eval_args[:2])
 
195
            assert ier==0,`ier`
 
196
            return z[:m]
 
197
        raise NotImplementedError,'finding roots unsupported for non-cubic splines'
 
198
 
 
199
class InterpolatedUnivariateSpline(UnivariateSpline):
 
200
    """ Interpolated univariate spline approximation."""
 
201
    def __init__(self, x, y, w=None, bbox = [None]*2, k=3):
 
202
        """
 
203
        Input:
 
204
          x,y   - 1-d sequences of data points (x must be
 
205
                  in strictly ascending order)
 
206
 
 
207
        Optional input:
 
208
          w          - positive 1-d sequence of weights
 
209
          bbox       - 2-sequence specifying the boundary of
 
210
                       the approximation interval.
 
211
                       By default, bbox=[x[0],x[-1]]
 
212
          k=3        - degree of the univariate spline.
 
213
        """
 
214
        #_data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier
 
215
        self._data = dfitpack.fpcurf0(x,y,k,w=w,
 
216
                                      xb=bbox[0],xe=bbox[1],s=0)
 
217
        self._reset_class()
 
218
 
 
219
class LSQUnivariateSpline(UnivariateSpline):
 
220
    """ Weighted least-squares univariate spline approximation."""
 
221
 
 
222
    def __init__(self, x, y, t, w=None, bbox = [None]*2, k=3):
 
223
        """
 
224
        Input:
 
225
          x,y   - 1-d sequences of data points (x must be
 
226
                  in strictly ascending order)
 
227
          t     - 1-d sequence of the positions of user-defined
 
228
                  interior knots of the spline (t must be in strictly
 
229
                  ascending order and bbox[0]<t[0]<...<t[-1]<bbox[-1])
 
230
 
 
231
        Optional input:
 
232
          w          - positive 1-d sequence of weights
 
233
          bbox       - 2-sequence specifying the boundary of
 
234
                       the approximation interval.
 
235
                       By default, bbox=[x[0],x[-1]]
 
236
          k=3        - degree of the univariate spline.
 
237
        """
 
238
        #_data == x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier
 
239
        xb=bbox[0]
 
240
        xe=bbox[1]
 
241
        if xb is None: xb = x[0]
 
242
        if xe is None: xe = x[-1]
 
243
        t = concatenate(([xb]*(k+1),t,[xe]*(k+1)))
 
244
        n = len(t)
 
245
        if not alltrue(t[k+1:n-k]-t[k:n-k-1] > 0):
 
246
            raise ValueError,\
 
247
                  'Interior knots t must satisfy Schoenberg-Whitney conditions'
 
248
        data = dfitpack.fpcurfm1(x,y,k,t,w=w,xb=xb,xe=xe)
 
249
        self._data = data[:-3] + (None,None,data[-1])
 
250
        self._reset_class()
 
251
 
 
252
 
 
253
################ Bivariate spline ####################
 
254
 
 
255
_surfit_messages = {1:"""
 
256
The required storage space exceeds the available storage space: nxest
 
257
or nyest too small, or s too small.
 
258
The weighted least-squares spline corresponds to the current set of
 
259
knots.""",
 
260
                    2:"""
 
261
A theoretically impossible result was found during the iteration
 
262
process for finding a smoothing spline with fp = s: s too small or
 
263
badly chosen eps.
 
264
Weighted sum of squared residuals does not satisfy abs(fp-s)/s < tol.""",
 
265
                    3:"""
 
266
the maximal number of iterations maxit (set to 20 by the program)
 
267
allowed for finding a smoothing spline with fp=s has been reached:
 
268
s too small.
 
269
Weighted sum of squared residuals does not satisfy abs(fp-s)/s < tol.""",
 
270
                    4:"""
 
271
No more knots can be added because the number of b-spline coefficients
 
272
(nx-kx-1)*(ny-ky-1) already exceeds the number of data points m:
 
273
either s or m too small.
 
274
The weighted least-squares spline corresponds to the current set of
 
275
knots.""",
 
276
                    5:"""
 
277
No more knots can be added because the additional knot would (quasi)
 
278
coincide with an old one: s too small or too large a weight to an
 
279
inaccurate data point.
 
280
The weighted least-squares spline corresponds to the current set of
 
281
knots.""",
 
282
                    10:"""
 
283
Error on entry, no approximation returned. The following conditions
 
284
must hold:
 
285
xb<=x[i]<=xe, yb<=y[i]<=ye, w[i]>0, i=0..m-1
 
286
If iopt==-1, then
 
287
  xb<tx[kx+1]<tx[kx+2]<...<tx[nx-kx-2]<xe
 
288
  yb<ty[ky+1]<ty[ky+2]<...<ty[ny-ky-2]<ye""",
 
289
                    -3:"""
 
290
The coefficients of the spline returned have been computed as the
 
291
minimal norm least-squares solution of a (numerically) rank deficient
 
292
system (deficiency=%i). If deficiency is large, the results may be
 
293
inaccurate. Deficiency may strongly depend on the value of eps."""
 
294
                    }
 
295
 
 
296
 
 
297
class BivariateSpline:
 
298
    """ Bivariate spline s(x,y) of degrees kx and ky on the rectangle
 
299
    [xb,xe] x [yb, ye] calculated from a given set of data points
 
300
    (x,y,z).
 
301
    """
 
302
 
 
303
    def get_residual(self):
 
304
        """ Return weighted sum of squared residuals of the spline
 
305
        approximation: sum ((w[i]*(z[i]-s(x[i],y[i])))**2)
 
306
        """
 
307
        return self.fp
 
308
    def get_knots(self):
 
309
        """ Return a tuple (tx,ty) where tx,ty contain knots positions
 
310
        of the spline with respect to x-, y-variable, respectively.
 
311
        The position of interior and additional knots are given as
 
312
          t[k+1:-k-1] and t[:k+1]=b, t[-k-1:]=e, respectively.
 
313
        """
 
314
        return self.tck[:2]
 
315
    def get_coeffs(self):
 
316
        """ Return spline coefficients."""
 
317
        return self.tck[2]
 
318
    def __call__(self,x,y,mth='array'):
 
319
        """ Evaluate spline at positions x,y."""
 
320
        if mth=='array':
 
321
            tx,ty,c = self.tck[:3]
 
322
            kx,ky = self.degrees
 
323
            z,ier = dfitpack.bispev(tx,ty,c,kx,ky,x,y)
 
324
            assert ier==0,'Invalid input: ier='+`ier`
 
325
            return z
 
326
        raise NotImplementedError
 
327
 
 
328
class SmoothBivariateSpline(BivariateSpline):
 
329
    """ Smooth bivariate spline approximation."""
 
330
 
 
331
    def __init__(self, x, y, z, w=None,
 
332
                 bbox = [None]*4, kx=3, ky=3, s=None, eps=None):
 
333
        """
 
334
        Input:
 
335
          x,y,z  - 1-d sequences of data points (order is not
 
336
                   important) 
 
337
        Optional input:
 
338
          w          - positive 1-d sequence of weights
 
339
          bbox       - 4-sequence specifying the boundary of
 
340
                       the rectangular approximation domain.
 
341
                       By default, bbox=[min(x,tx),max(x,tx),min(y,ty),max(y,ty)]
 
342
          kx,ky=3,3  - degrees of the bivariate spline.
 
343
          s          - positive smoothing factor defined for
 
344
                       estimation condition:
 
345
                         sum((w[i]*(z[i]-s(x[i],y[i])))**2) <= s
 
346
                       Default s=len(w) which should be a good value
 
347
                       if 1/w[i] is an estimate of the standard
 
348
                       deviation of z[i].
 
349
          eps        - a threshold for determining the effective rank
 
350
                       of an over-determined linear system of
 
351
                       equations. 0 < eps < 1, default is 1e-16.
 
352
        """
 
353
        xb,xe,yb,ye = bbox
 
354
        nx,tx,ny,ty,c,fp,wrk1,ier = dfitpack.surfit_smth(x,y,z,w,\
 
355
                                                         xb,xe,yb,ye,\
 
356
                                                         kx,ky,s=s,eps=eps,lwrk2=1)
 
357
        if ier in [0,-1,-2]: # normal return
 
358
            pass
 
359
        else:
 
360
            message = _surfit_messages.get(ier,'ier=%s' % (ier))
 
361
            warnings.warn(message)
 
362
 
 
363
        self.fp = fp
 
364
        self.tck = tx[:nx],ty[:ny],c[:(nx-kx-1)*(ny-ky-1)]
 
365
        self.degrees = kx,ky
 
366
 
 
367
class LSQBivariateSpline(BivariateSpline):
 
368
    """ Weighted least-squares spline approximation.
 
369
    """
 
370
    def __init__(self, x, y, z, tx, ty, w=None,
 
371
                 bbox = [None]*4,
 
372
                 kx=3, ky=3, eps=None):
 
373
        """
 
374
        Input:
 
375
          x,y,z  - 1-d sequences of data points (order is not
 
376
                   important) 
 
377
          tx,ty  - strictly ordered 1-d sequences of knots
 
378
                   coordinates. 
 
379
        Optional input:
 
380
          w          - positive 1-d sequence of weights
 
381
          bbox       - 4-sequence specifying the boundary of
 
382
                       the rectangular approximation domain.
 
383
                       By default, bbox=[min(x,tx),max(x,tx),min(y,ty),max(y,ty)]
 
384
          kx,ky=3,3  - degrees of the bivariate spline.
 
385
          eps        - a threshold for determining the effective rank
 
386
                       of an over-determined linear system of
 
387
                       equations. 0 < eps < 1, default is 1e-16. 
 
388
        """
 
389
        nx = 2*kx+2+len(tx)
 
390
        ny = 2*ky+2+len(ty)
 
391
        tx1 = zeros((nx,),Float)
 
392
        ty1 = zeros((ny,),Float)
 
393
        tx1[kx+1:nx-kx-1] = tx
 
394
        ty1[ky+1:ny-ky-1] = ty
 
395
 
 
396
        xb,xe,yb,ye = bbox
 
397
        tx1,ty1,c,fp,ier = dfitpack.surfit_lsq(x,y,z,tx1,ty1,w,\
 
398
                                               xb,xe,yb,ye,\
 
399
                                               kx,ky,eps,lwrk2=1)
 
400
        if ier>10:
 
401
            tx1,ty1,c,fp,ier = dfitpack.surfit_lsq(x,y,z,tx1,ty1,w,\
 
402
                                                   xb,xe,yb,ye,\
 
403
                                                   kx,ky,eps,lwrk2=ier)
 
404
        if ier in [0,-1,-2]: # normal return
 
405
            pass
 
406
        else:
 
407
            if ier<-2:
 
408
                deficiency = (nx-kx-1)*(ny-ky-1)+ier
 
409
                message = _surfit_messages.get(-3) % (deficiency)
 
410
            else:
 
411
                message = _surfit_messages.get(ier,'ier=%s' % (ier))
 
412
            warnings.warn(message)
 
413
        self.fp = fp
 
414
        self.tck = tx1,ty1,c
 
415
        self.degrees = kx,ky
 
416