From 8fb17b22fd1903e622f90acd10cafe146fe3e1ec Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Mon, 13 Nov 2023 18:46:46 +0100 Subject: [PATCH] fit results save weight; closes #139 --- src/nmreval/fit/data.py | 21 ++++++----- src/nmreval/fit/minimizer.py | 15 ++++++-- src/nmreval/fit/result.py | 71 ++++++++++++++++++++++++++++++------ 3 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/nmreval/fit/data.py b/src/nmreval/fit/data.py index 4a34409..f6f2cc5 100644 --- a/src/nmreval/fit/data.py +++ b/src/nmreval/fit/data.py @@ -13,7 +13,7 @@ class Data(object): if self.y.shape[0] != self.x.shape[0]: raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}') - self.we = self._calc_weights(we) + self.we, self.we_string = self._calc_weights(we) self.idx = idx self.model = None self.minimizer = None @@ -24,16 +24,16 @@ class Data(object): def __len__(self): return self.y.shape[0] - def _calc_weights(self, we): - if we is None: - return 1. - - if isinstance(we, str): - if we == 'y2': - we_func = lambda yy: 1. / yy**2 - elif we.lower() == 'none': + def _calc_weights(self, we: str | np.ndarray | None) -> tuple[np.ndarray, str]: + if isinstance(we, str) or we is None: + if we is None or we.lower() == 'none': + we_string = 'None' we_func = lambda yy: np.ones_like(len(yy)) + elif we == 'y2': + we_string = we + we_func = lambda yy: 1. / yy**2 else: + we_string = we we_func = lambda yy: 1. / np.abs(yy) if np.iscomplexobj(self.y): @@ -42,6 +42,7 @@ class Data(object): weights = we_func(self.y) else: + we_string = 'yerr' # This is pure speculation that array equals error we = 1. / np.asarray(we) if np.iscomplexobj(self.y): if np.iscomplexobj(we): @@ -53,7 +54,7 @@ class Data(object): weights[weights == np.inf] = np.finfo(float).max - return weights + return weights, we_string def set_model(self, func, *args, **kwargs): if isinstance(func, Model): diff --git a/src/nmreval/fit/minimizer.py b/src/nmreval/fit/minimizer.py index fb6bcc5..40a7574 100644 --- a/src/nmreval/fit/minimizer.py +++ b/src/nmreval/fit/minimizer.py @@ -484,9 +484,18 @@ class FitRoutine(object): idx = self.data.index(data) model = data.get_model() - self.result[idx] = FitResultCreator.make_with_model(model, data.x, data.y, - actual_parameters, data.fun_kwargs, data.idx, - *shape, corr=actual_corr, pcorr=actual_pcorr) + self.result[idx] = FitResultCreator.make_with_model( + model, + data.x, + data.y, + actual_parameters, + data.fun_kwargs, + data.we_string, + data.idx, + *shape, + corr=actual_corr, + pcorr=actual_pcorr, + ) return self.result diff --git a/src/nmreval/fit/result.py b/src/nmreval/fit/result.py index b460ad8..37d513f 100644 --- a/src/nmreval/fit/result.py +++ b/src/nmreval/fit/result.py @@ -42,7 +42,19 @@ class FitResultCreator: kwargs['name'], stats, idx) @staticmethod - def make_with_model(model, x_orig, y_orig, p, fun_kwargs, idx, nobs, nvar, corr, pcorr) -> FitResult: + def make_with_model( + model: 'Model', + x_orig: np.ndarray, + y_orig: np.ndarray, + p: 'Parameters', + fun_kwargs: dict, + we: str, + idx: str | None, + nobs: int, + nvar: int, + corr: np.ndarray, + pcorr: np.ndarray, + ) -> FitResult: if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)): islog = True else: @@ -104,10 +116,25 @@ class FitResultCreator: correlation = corr partial_correlation = pcorr - return FitResult(_x, _y, x_orig, y_orig, parameters, fun_kwargs, resid, - nobs, nvar, model.name, stats, - idx=idx, corr=correlation, pcorr=partial_correlation, - islog=islog, func=model) + return FitResult( + x=_x, + y=_y, + x_data=x_orig, + y_data=y_orig, + params=parameters, + fun_kwargs=fun_kwargs, + resid=resid, + nobs=nobs, + nvar=nvar, + we=we, + name=model.name, + stats=stats, + idx=idx, + corr=correlation, + pcorr=partial_correlation, + islog=islog, + func=model, + ) @staticmethod def calc_statistics(y, residual, nobs=None, nvar=None): @@ -145,12 +172,27 @@ class FitResultCreator: class FitResult(Points): - def __init__(self, x: np.ndarray, y: np.ndarray, - x_data: np.ndarray, y_data: np.ndarray, - params: dict, fun_kwargs: dict, - resid: np.ndarray, nobs: int, nvar: int, name: str, stats: dict, - idx=None, corr=None, pcorr=None, islog=False, func=None, - **kwargs): + def __init__( + self: FitResult, + x: np.ndarray, + y: np.ndarray, + x_data: np.ndarray, + y_data: np.ndarray, + params: dict, + fun_kwargs: dict, + resid: np.ndarray, + nobs: int, + nvar: int, + we: str, + name: str, + stats: dict, + idx: int = None, + corr: np.ndarray = None, + pcorr: np.ndarray = None, + islog: bool = False, + func=None, + **kwargs + ): self.parameter, name = self._prepare_names(params, name) label = kwargs.get('label', name) @@ -161,6 +203,7 @@ class FitResult(Points): self.statistics = stats self.nobs = nobs self.nvar = nvar + self.we = we self.fun_kwargs = fun_kwargs self.correlation = corr self.partial_correlation = pcorr @@ -229,6 +272,7 @@ class FitResult(Points): s.write(f' model : {self.name}\n') s.write(f' #data : {self.nobs}\n') s.write(f' #var : {self.nvar}\n') + s.write(f' #weight: {self.we}\n') s.write('\nParameter\n') s.write(self.parameter_string()) @@ -322,10 +366,13 @@ class FitResult(Points): err = 0. f.write(f'{p.value:.8e}\t{err:.8e}\t') + f.write('# ') if self.fun_kwargs: - f.write('# ') for k, v in self.fun_kwargs.items(): f.write(f"{convert(k, old='tex', new='str')}: {convert(str(v), old='tex', new='str')}\t") + + f.write(f'weight: {self.we}\t') + f.write('\n') f.write(f'# line above from: {self.name} (model: {self.model_name})') f.write('\n')