"""Abstract base class for Decorator functions for convenient fitting"""
# Encoding: utf-8
from abc import ABC, abstractmethod
import pandas as pd
try:
from lmfit import Parameters
from ipywidgets import widgets
except ImportError as e:
raise ImportError(
"This module requires lmfit and ipywidgets to work properly.\n"
"Try installing this package with the additional fitting requirement, "
"i.e. pip install pyElli[fitting]"
) from e
from .params_hist import ParamsHist
[docs]def is_in_notebook() -> bool:
"""Checks whether the current shell is in a jupyter notebook.
Returns:
bool: True if the shell is in jupyter, False otherwise.
"""
try:
# pylint: disable=import-outside-toplevel
from IPython import get_ipython
if "IPKernelApp" not in get_ipython().config:
return False
except ImportError:
return False
except AttributeError:
return False
return True
[docs]class FitDecorator(ABC):
"""The abstract base class for fitting decorators.
Providing features for fit, undo and redo buttons."""
def __init__(self) -> None:
self.params = ParamsHist()
self.fitted_params = ParamsHist()
self.last_params = ParamsHist()
self.initial_params = ParamsHist()
self.param_widgets = {}
self.fit_kwargs = {}
[docs] @abstractmethod
def get_model_data(
self, params: Parameters = None, append_exp_data=False
) -> pd.DataFrame:
"""Gets the data from the provided model with the provided parameters.
If no parameters are provided, the fitted parameters are used
(which default to the initial parameters if no fit has been triggered).
Args:
params (Parameters, optional):
The parameters to calculate the model with.
If not provided, the fitted parameters are used.
Defaults to None.
append_exp_data (bool, optional):
Appends the experimental data if set to True.
Defaults to False.
Returns:
pd.DataFrame: The model results
"""
[docs] def to_csv(
self,
*args,
fname: str,
params: Parameters = None,
append_exp_data: bool = False,
**kwargs,
) -> None:
"""Saves the current model to csv. This is just a wrapper to
pandas Dataframe and any argument to pandas to_csv may be passed
as function arguments.
Args:
fname (str): The file name to save the data to.
params (Parameters, optional):
The parameters to calculate the model with.
If not provided, the fitted parameters are used.
Defaults to None.
append_exp_data (bool, optional):
Appends the experimental data if set to True.
Defaults to False.
"""
self.get_model_data(params, append_exp_data).to_csv(fname, *args, **kwargs)
[docs] @abstractmethod
def fit(self, method: str = "") -> None:
"""Execute lmfit with the current fitting parameters
Args:
method (str, optional): The fitting method to use.
Any method supported by scipys curve_fit is allowed.
Defaults to 'leastsq'.
Returns:
Result: The fitting result
"""
[docs] @abstractmethod
def update_selection(self, change: dict = None) -> None:
"""Update plot after selection of displayed data
Args:
change (dict, optional): A dictionary containing the ipywidgets change event
"""
def set_vary_param(self, change: dict) -> None:
self.initial_params[change.owner.description_tooltip].vary = change.new
self.params[change.owner.description_tooltip].vary = change.new
[docs] def reset_to_init_params(self) -> None:
"""Resets the parameters to the initial values"""
if not isinstance(self.params, ParamsHist):
return
self.last_params = self.params.pop()
self.params = self.initial_params.copy()
self.update_widgets()
self.update_selection()
[docs] def update_params(self, change: dict) -> None:
"""Update plot after a change of fitting parameters
Args:
change (dict): A dictionary containing the ipywidgets change event
selected (dict): The selected value of the data display dropdown widget
"""
if isinstance(self.params, ParamsHist):
self.params.commit()
self.params[change.owner.description].value = change.new
self.update_selection()