From 6137a8fc62d08cb28c540ad86524b37246511525 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 1 Jun 2010 22:10:36 -0400 Subject: [PATCH] Additional cleanups in fit.py * Added outqueue to tests, which turned up an 'outqeue' typo. * Store info in .set_data(). This allows subclasses to write data-specific information when the data changes. * .guess_scale() returns Null by default. This tells leastsq to use its internal scale estimating magic. * Added strictly positive scale assertion. The error message returned by leastsq for zero or negative scales isn't very obvious. --- hooke/util/fit.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/hooke/util/fit.py b/hooke/util/fit.py index e3f23f4..da120d7 100644 --- a/hooke/util/fit.py +++ b/hooke/util/fit.py @@ -40,6 +40,8 @@ class ModelFitter (object): Examples -------- + >>> from pprint import pprint + >>> from Queue import Queue >>> import numpy You'll want to subclass `ModelFitter`, overriding at least @@ -77,7 +79,22 @@ class ModelFitter (object): ... return [slope_scale, offset_scale] >>> data = 20*numpy.sin(arange(1000)) + 7.*arange(1000) - 33.0 >>> m = LinearModel(data) - >>> slope,offset = m.fit() + >>> outqueue = Queue() + >>> slope,offset = m.fit(outqueue=outqueue) + >>> info = outqueue.get() + >>> pprint(info) # doctest: +ELLIPSIS, +REPORT_UDIFF + {'convergence flag': 2, + 'covariance matrix': array([[ 1.199...e-08, -5.993...e-06], + [ -5.993...e-06, 3.994...e-03]]), + 'fitted parameters': array([ 6.999..., -32.889...]), + 'info': {'fjac': array([[...]]), + 'fvec': array([...]), + 'ipvt': array([1, 2]), + 'nfev': 7, + 'qtf': array([ 2.851...e-07, 1.992...e-06])}, + 'initial parameters': [6.992..., -33.0], + 'message': 'The relative error between two consecutive iterates is at most 0.000...', + 'scale': [0.699..., 202.071...]} We round the outputs to protect the doctest against differences in machine rounding during computation. We expect the values to be close @@ -92,12 +109,12 @@ class ModelFitter (object): :math:`2\pi`. """ def __init__(self, data, info=None): - self.set_data(data) - self.info = info + self.set_data(data, info) - def set_data(self, data): + def set_data(self, data, info=None): self._data = data self._model_data = ndarray(shape=data.shape, dtype=data.dtype) + self.info = info def model(self, params): p = params # convenient alias @@ -108,7 +125,7 @@ class ModelFitter (object): return [] def guess_scale(self, params, outqueue=None): - return [] + return None def residual(self, params): return self._data - self.model(params) @@ -135,11 +152,12 @@ class ModelFitter (object): initial_params = self.guess_initial_params(outqueue) if scale == None: scale = self.guess_scale(initial_params, outqueue) + assert min(scale) > 0, scale params,cov,info,mesg,ier = leastsq( func=self.residual, x0=initial_params, full_output=True, diag=scale, **kwargs) if outqueue != None: - outqeue.put({ + outqueue.put({ 'initial parameters': initial_params, 'scale': scale, 'fitted parameters': params, -- 2.26.2