Additional cleanups in fit.py
authorW. Trevor King <wking@drexel.edu>
Wed, 2 Jun 2010 02:10:36 +0000 (22:10 -0400)
committerW. Trevor King <wking@drexel.edu>
Wed, 2 Jun 2010 02:10:36 +0000 (22:10 -0400)
* 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

index e3f23f4387c5b492ac38af36dddc460fa435c8aa..da120d730115db98787ae93caae45d7e2ee475ee 100644 (file)
@@ -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,