-# Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
+# Copyright (C) 2010-2012 W. Trevor King <wking@tremily.us>
#
# This file is part of Hooke.
#
-# Hooke is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# Hooke is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
#
-# Hooke is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
-# Public License for more details.
+# Hooke is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
#
-# You should have received a copy of the GNU Lesser General Public
-# License along with Hooke. If not, see
-# <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with Hooke. If not, see <http://www.gnu.org/licenses/>.
"""Provide :class:`ModelFitter` to make arbitrary model fitting easy.
"""
from numpy import arange, ndarray
+from scipy import __version__ as _scipy_version
from scipy.optimize import leastsq
-import scipy.optimize
+
+_strings = _scipy_version.split('.')
+# Don't convert third string to an integer in case of (for example) '0.7.2rc3'
+_SCIPY_VERSION = (int(_strings[0]), int(_strings[1]), _strings[2])
+del _strings
class PoorFit (ValueError):
>>> outqueue = Queue()
>>> slope,offset = m.fit(outqueue=outqueue)
>>> info = outqueue.get(block=False)
- >>> pprint(info) # doctest: +ELLIPSIS, +REPORT_UDIFF
+ >>> pprint(info) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_UDIFF
{'active fitted parameters': array([ 6.999..., -32.889...]),
'active parameters': array([ 6.999..., -32.889...]),
- 'convergence flag': 2,
+ 'convergence flag': ...,
'covariance matrix': array([[ 1.199...e-08, -5.993...e-06],
[ -5.993...e-06, 3.994...e-03]]),
'data scale factor': 1.0,
'fvec': array([...]),
'ipvt': array([1, 2]),
'nfev': 7,
- 'qtf': array([ 2.851...e-07, 1.992...e-06])},
+ 'qtf': array([...])},
'initial parameters': [6.992..., -33.0],
- 'message': 'The relative error between two consecutive iterates is at most 0.000...',
+ 'message': '...relative error between two consecutive iterates is at most 0.000...',
'rescaled': False,
'scale': [0.100..., 6.123...]}
machine rounding during computation. We expect the values to be close
to the input settings (slope 7, offset -33).
- >>> print '%.3f' % slope
+ >>> print('{:.3f}'.format(slope))
7.000
- >>> print '%.3f' % offset
+ >>> print('{:.3f}'.format(offset))
-32.890
The offset is a bit off because, the range is not a multiple of
>>> m = LinearModel(data, rescale=True)
>>> outqueue = Queue()
>>> slope,offset = m.fit(outqueue=outqueue)
- >>> print '%.3f' % slope
+ >>> print('{:.3f}'.format(slope))
7.000
- >>> print '%.3f' % offset
+ >>> print('{:.3f}'.format(offset))
-32.890
+
+ Test single-parameter models:
+
+ >>> class SingleParameterModel (LinearModel):
+ ... '''Simple linear model.
+ ... '''
+ ... def model(self, params):
+ ... return super(SingleParameterModel, self).model([params[0], 0.])
+ ... def guess_initial_params(self, outqueue=None):
+ ... return super(SingleParameterModel, self
+ ... ).guess_initial_params(outqueue)[:1]
+ ... def guess_scale(self, params, outqueue=None):
+ ... return super(SingleParameterModel, self
+ ... ).guess_scale([params[0], 0.], outqueue)[:1]
+ >>> data = 20*numpy.sin(arange(1000)) + 7.*arange(1000)
+ >>> m = SingleParameterModel(data)
+ >>> slope, = m.fit(outqueue=outqueue)
+ >>> print('{:.3f}'.format(slope))
+ 7.000
"""
def __init__(self, *args, **kwargs):
self.set_data(*args, **kwargs)
params,cov,info,mesg,ier = leastsq(
func=self.residual, x0=active_params, full_output=True,
diag=scale, **kwargs)
+ if len(initial_params) == 1 and _SCIPY_VERSION < (0, 8, '0'):
+ # params is a float for scipy < 0.8.0. Convert to list.
+ params = [params]
if self._rescale == True:
active_params = params
- if len(initial_params) == 1: # params is a float
- params = params * self._param_scale_factors[0]
- else:
- params = [p*s for p,s in zip(params,
- self._param_scale_factors)]
+ params = [p*s for p,s in zip(params,
+ self._param_scale_factors)]
else:
active_params = params
if outqueue != None:
# def dist(px,py,linex,liney):
# distancesx=scipy.array([(px-x)**2 for x in linex])
# minindex=numpy.argmin(distancesx)
-# print px, linex[0], linex[-1]
+# print(px, linex[0], linex[-1])
# return (py-liney[minindex])**2
#
#