lmfit/lmfit-py — pull request #123 by danielballan (2014-09-09 14:35:48+00:00)
Title: Fitter

This may not belong in the 0.8 release, but it's an exciting day's work, and I want to share it.

The purpose of `Fitter` is to offer a curve-fitting workflow akin to popular GUI-based tools while retaining programmatic access to the underlying objects.

On a basic level, `Fitter` provides the "stateful" interaction that some desire or expect from `Model`. It keeps a copy of the Parameters called `current_params`, and it updates them with the latest best fit each time `fit()` is called. If the model supports automatic guessing or has been run at least once, `fit()` may be called without arguments. New data can be plugged in, and the parameters can be reused on the new data.

>>> fitter = Fitter(data, model=models.LinearModel, x=x)
# guess is automatically performed (if possible)
>>> fitter.current_params # a "stateful" copy of the params reflecting the latest guess or fit
>>> fitter.fit()
>>> fitter.current_result # the result, which will be used to generate the next guess...
>>> fitter.data = some_other_data
>>> fitter.fit()
>>> fitter.current_result # new result

I still feel that this "stateful" behavior definitely does not belong in `Model` itself, but it is useful as part of a higher-level object. `Fitter` is that object.

`Fitter` does not introduce any new required dependencies. But if IPython is available, a subclass of `Fitter` is used, introducing richer features. It updates a matplotlib plot after each fit, displaying the data points, the initial guess in the gray (derived either from `current_params` or from arguments passed to `fit()`), and the best fit in red.

In the browser-based IPython notebook, it represents the Parameters as widgets like so:

![screen shot 2014-09-09 at 10 07 23 am](https://cloud.githubusercontent.com/assets/2279598/4202867/a6692036-382b-11e4-9320-cceb017b1f15.png)
You can [download and run this notebook](http://nbviewer.ipython.org/gist/anonymous/a07891d39015b0034e56) to try it out. Unless you run it locally, you will not be able to see or interact with the widget interface shown in the screenshot above. ("Persistent widgets" that do not have this limitation will arrive later this year in IPython 3.0, which this PR already supports.)

A dropdown menu of models can swap the `fitter.model` for easy experimentation. The menu includes every subclass of `lmfit.Model`, including both the full contents of `lmfit.models` and any subclasses that the user has defined up to the moment the `fitter` is instantiated.

This is just one approach to the GUI-based interactivity discussed in #17. The basic structure is `Fitter` is contained in a base class completely free of any IPython-related code; all of those particulars are cordoned off in a subclass. I personally like working in notebooks, but others could build completely different GUIs onto the `Fitter` base class.

Perhaps this could be introduced as an experimental feature in 0.8.1 in the near future.