From 97990180669352b52582b5d9e1c234f439f8e96c Mon Sep 17 00:00:00 2001 From: IvanARashid Date: Mon, 8 Jul 2024 15:06:35 +0200 Subject: [PATCH] Standardized fit results as dictionaries. Modified all of fitting algorithms as well. Including slight modification to testing script --- src/standardized/ETP_SRI_LinearFitting.py | 16 ++++++++++--- src/standardized/IAR_LU_biexp.py | 9 +++---- src/standardized/IAR_LU_modified_mix.py | 12 ++++++---- src/standardized/IAR_LU_modified_topopro.py | 14 +++++++---- src/standardized/IAR_LU_segmented_2step.py | 14 +++++++---- src/standardized/IAR_LU_segmented_3step.py | 14 +++++++---- src/standardized/IAR_LU_subtracted.py | 14 +++++++---- .../OGC_AmsterdamUMC_Bayesian_biexp.py | 9 +++---- src/standardized/OGC_AmsterdamUMC_biexp.py | 9 +++---- .../OGC_AmsterdamUMC_biexp_segmented.py | 9 +++---- src/standardized/OJ_GU_seg.py | 11 +++++---- src/standardized/PV_MUMC_biexp.py | 11 +++++---- src/standardized/PvH_KB_NKI_IVIMfit.py | 9 +++---- src/wrappers/OsipiBase.py | 24 ++++++++++++++++--- tests/IVIMmodels/unit_tests/test_ivim_fit.py | 9 +++---- 15 files changed, 124 insertions(+), 60 deletions(-) diff --git a/src/standardized/ETP_SRI_LinearFitting.py b/src/standardized/ETP_SRI_LinearFitting.py index da0eb5e..a5d45c5 100644 --- a/src/standardized/ETP_SRI_LinearFitting.py +++ b/src/standardized/ETP_SRI_LinearFitting.py @@ -64,11 +64,21 @@ def ivim_fit(self, signals, bvalues=None, linear_fit_option=False, **kwargs): ETP_object = LinearFit() else: ETP_object = LinearFit(self.thresholds[0]) - + + results = {} if linear_fit_option: f, Dstar = ETP_object.linear_fit(bvalues, signals) - return f, Dstar + + results["f"] = f + results["D*"] = Dstar + + return results else: f, D, Dstar = ETP_object.ivim_fit(bvalues, signals) - return f, Dstar, D + + results["f"] = f + results["D*"] = Dstar + results["D"] = D + + return results diff --git a/src/standardized/IAR_LU_biexp.py b/src/standardized/IAR_LU_biexp.py index 7389f8c..110413a 100644 --- a/src/standardized/IAR_LU_biexp.py +++ b/src/standardized/IAR_LU_biexp.py @@ -76,8 +76,9 @@ def ivim_fit(self, signals, bvalues, **kwargs): fit_results = self.IAR_algorithm.fit(signals) - f = fit_results.model_params[1] - Dstar = fit_results.model_params[2] - D = fit_results.model_params[3] + results = {} + results["f"] = fit_results.model_params[1] + results["D*"] = fit_results.model_params[2] + results["D"] = fit_results.model_params[3] - return f, Dstar, D \ No newline at end of file + return results \ No newline at end of file diff --git a/src/standardized/IAR_LU_modified_mix.py b/src/standardized/IAR_LU_modified_mix.py index deefde1..9571a56 100644 --- a/src/standardized/IAR_LU_modified_mix.py +++ b/src/standardized/IAR_LU_modified_mix.py @@ -76,8 +76,12 @@ def ivim_fit(self, signals, bvalues, **kwargs): fit_results = self.IAR_algorithm.fit(signals) - f = fit_results.model_params[1] - Dstar = fit_results.model_params[2] - D = fit_results.model_params[3] + #f = fit_results.model_params[1] + #Dstar = fit_results.model_params[2] + #D = fit_results.model_params[3] + results = {} + results["f"] = fit_results.model_params[1] + results["D*"] = fit_results.model_params[2] + results["D"] = fit_results.model_params[3] - return f, Dstar, D \ No newline at end of file + return results \ No newline at end of file diff --git a/src/standardized/IAR_LU_modified_topopro.py b/src/standardized/IAR_LU_modified_topopro.py index 91bf9d8..9e39fc2 100644 --- a/src/standardized/IAR_LU_modified_topopro.py +++ b/src/standardized/IAR_LU_modified_topopro.py @@ -76,8 +76,14 @@ def ivim_fit(self, signals, bvalues, **kwargs): fit_results = self.IAR_algorithm.fit(signals) - f = fit_results.model_params[1] - Dstar = fit_results.model_params[2] - D = fit_results.model_params[3] + #f = fit_results.model_params[1] + #Dstar = fit_results.model_params[2] + #D = fit_results.model_params[3] - return f, Dstar, D \ No newline at end of file + #return f, Dstar, D + results = {} + results["f"] = fit_results.model_params[1] + results["D*"] = fit_results.model_params[2] + results["D"] = fit_results.model_params[3] + + return results \ No newline at end of file diff --git a/src/standardized/IAR_LU_segmented_2step.py b/src/standardized/IAR_LU_segmented_2step.py index 0365b5f..3fb5cdd 100644 --- a/src/standardized/IAR_LU_segmented_2step.py +++ b/src/standardized/IAR_LU_segmented_2step.py @@ -77,8 +77,14 @@ def ivim_fit(self, signals, bvalues, thresholds=None, **kwargs): fit_results = self.IAR_algorithm.fit(signals) - f = fit_results.model_params[1] - Dstar = fit_results.model_params[2] - D = fit_results.model_params[3] + #f = fit_results.model_params[1] + #Dstar = fit_results.model_params[2] + #D = fit_results.model_params[3] - return f, Dstar, D \ No newline at end of file + #return f, Dstar, D + results = {} + results["f"] = fit_results.model_params[1] + results["D*"] = fit_results.model_params[2] + results["D"] = fit_results.model_params[3] + + return results \ No newline at end of file diff --git a/src/standardized/IAR_LU_segmented_3step.py b/src/standardized/IAR_LU_segmented_3step.py index 6630f77..170b8ee 100644 --- a/src/standardized/IAR_LU_segmented_3step.py +++ b/src/standardized/IAR_LU_segmented_3step.py @@ -76,8 +76,14 @@ def ivim_fit(self, signals, bvalues, **kwargs): fit_results = self.IAR_algorithm.fit(signals) - f = fit_results.model_params[1] - Dstar = fit_results.model_params[2] - D = fit_results.model_params[3] + #f = fit_results.model_params[1] + #Dstar = fit_results.model_params[2] + #D = fit_results.model_params[3] - return f, Dstar, D \ No newline at end of file + #return f, Dstar, D + results = {} + results["f"] = fit_results.model_params[1] + results["D*"] = fit_results.model_params[2] + results["D"] = fit_results.model_params[3] + + return results \ No newline at end of file diff --git a/src/standardized/IAR_LU_subtracted.py b/src/standardized/IAR_LU_subtracted.py index 9db0a03..d5508f2 100644 --- a/src/standardized/IAR_LU_subtracted.py +++ b/src/standardized/IAR_LU_subtracted.py @@ -76,8 +76,14 @@ def ivim_fit(self, signals, bvalues, **kwargs): fit_results = self.IAR_algorithm.fit(signals) - f = fit_results.model_params[1] - Dstar = fit_results.model_params[2] - D = fit_results.model_params[3] + #f = fit_results.model_params[1] + #Dstar = fit_results.model_params[2] + #D = fit_results.model_params[3] - return f, Dstar, D \ No newline at end of file + #return f, Dstar, D + results = {} + results["f"] = fit_results.model_params[1] + results["D*"] = fit_results.model_params[2] + results["D"] = fit_results.model_params[3] + + return results \ No newline at end of file diff --git a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py index dcbb629..1b8c8f9 100644 --- a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py @@ -78,8 +78,9 @@ def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs): fit_results=fit_results+(1,) fit_results = self.OGC_algorithm(bvalues, signals, self.neg_log_prior, x0=fit_results, fitS0=self.fitS0) - D = fit_results[0] - f = fit_results[1] - Dstar = fit_results[2] + results = {} + results["D"] = fit_results[0] + results["f"] = fit_results[1] + results["D*"] = fit_results[2] - return f, Dstar, D \ No newline at end of file + return results \ No newline at end of file diff --git a/src/standardized/OGC_AmsterdamUMC_biexp.py b/src/standardized/OGC_AmsterdamUMC_biexp.py index d61a50e..66b9e6c 100644 --- a/src/standardized/OGC_AmsterdamUMC_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_biexp.py @@ -68,8 +68,9 @@ def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs): bvalues=np.array(bvalues) fit_results = self.OGC_algorithm(bvalues, signals, p0=self.initial_guess, bounds=self.bounds, fitS0=self.fitS0) - D = fit_results[0] - f = fit_results[1] - Dstar = fit_results[2] + results = {} + results["D"] = fit_results[0] + results["f"] = fit_results[1] + results["D*"] = fit_results[2] - return f, Dstar, D \ No newline at end of file + return results \ No newline at end of file diff --git a/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py b/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py index cae609c..c452f7b 100644 --- a/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py +++ b/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py @@ -69,8 +69,9 @@ def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs): bvalues=np.array(bvalues) fit_results = self.OGC_algorithm(bvalues, signals, bounds=self.bounds, cutoff=self.thresholds, p0=self.initial_guess) - D = fit_results[0] - f = fit_results[1] - Dstar = fit_results[2] + results = {} + results["D"] = fit_results[0] + results["f"] = fit_results[1] + results["D*"] = fit_results[2] - return f, Dstar, D \ No newline at end of file + return results \ No newline at end of file diff --git a/src/standardized/OJ_GU_seg.py b/src/standardized/OJ_GU_seg.py index 03a3cd0..af40be7 100644 --- a/src/standardized/OJ_GU_seg.py +++ b/src/standardized/OJ_GU_seg.py @@ -63,9 +63,10 @@ def ivim_fit(self, signals, bvalues=None): bthr = self.thresholds[0] fit_results = seg(signals, bvalues, bthr) + + results = {} + results["f"] = fit_results['f'] + results["D*"] = fit_results['Dstar'] + results["D"] = fit_results['D'] - f = fit_results['f'] - Dstar = fit_results['Dstar'] - D = fit_results['D'] - - return f, Dstar, D \ No newline at end of file + return results \ No newline at end of file diff --git a/src/standardized/PV_MUMC_biexp.py b/src/standardized/PV_MUMC_biexp.py index 1425aee..f08ddd1 100644 --- a/src/standardized/PV_MUMC_biexp.py +++ b/src/standardized/PV_MUMC_biexp.py @@ -48,9 +48,10 @@ def ivim_fit(self, signals, bvalues=None): fit_results = self.PV_algorithm(bvalues, signals) + + results = {} + results["f"] = fit_results[1] + results["D*"] = fit_results[2] + results["D"] = fit_results[0] - f = fit_results[1] - Dstar = fit_results[2] - D = fit_results[0] - - return f, Dstar, D + return results diff --git a/src/standardized/PvH_KB_NKI_IVIMfit.py b/src/standardized/PvH_KB_NKI_IVIMfit.py index 3d21ad5..52eea8d 100644 --- a/src/standardized/PvH_KB_NKI_IVIMfit.py +++ b/src/standardized/PvH_KB_NKI_IVIMfit.py @@ -55,8 +55,9 @@ def ivim_fit(self, signals, bvalues=None): signals = np.reshape(signals, (1, 1, 1, len(signals))) # assuming that in this test the signals are always single voxel fit_results = self.NKI_algorithm(signals,bvalues) - D = fit_results[0][0,0,0]/1000 - f = fit_results[1][0,0,0] - Dstar = fit_results[2][0,0,0]/1000 + results = {} + results["D"] = fit_results[0][0,0,0]/1000 + results["f"] = fit_results[1][0,0,0] + results["D*"] = fit_results[2][0,0,0]/1000 - return f, Dstar, D + return results diff --git a/src/wrappers/OsipiBase.py b/src/wrappers/OsipiBase.py index d889b7d..405ed3b 100644 --- a/src/wrappers/OsipiBase.py +++ b/src/wrappers/OsipiBase.py @@ -84,12 +84,30 @@ def osipi_fit(self, data, bvalues=None, **kwargs): #args = [data, use_bvalues, use_initial_guess, use_bounds, use_thresholds] #args = [arg for arg in args if arg is not None] + # Check if there is an attribute that defines the result dictionary keys + if hasattr(self, "result_keys"): + # result_keys is a list of strings of parameter names, e.g. "S0", "f1", "f2", etc. + result_keys = self.result_keys + else: + # Default is ["f", "D*", "D"] + self.result_keys = ["f", "D*", "D"] + + results = {} + for key in self.result_keys: + results[key] = np.empty(list(data.shape[:-1])) + # Assuming the last dimension of the data is the signal values of each b-value - results = np.empty(list(data.shape[:-1])+[3]) # Create an array with the voxel dimensions + the ones required for the fit + #results = np.empty(list(data.shape[:-1])+[3]) # Create an array with the voxel dimensions + the ones required for the fit + #for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])): + #args = [data[ijk], use_bvalues] + #fit = list(self.ivim_fit(*args, **kwargs)) + #results[ijk] = fit + for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])): args = [data[ijk], use_bvalues] - fit = list(self.ivim_fit(*args, **kwargs)) - results[ijk] = fit + fit = self.ivim_fit(*args, **kwargs) # For single voxel fits, we assume this is a dict with a float value per key. + for key in list(fit.keys()): + results[key][ijk] = fit[key] #self.parameter_estimates = self.ivim_fit(data, bvalues) return results diff --git a/tests/IVIMmodels/unit_tests/test_ivim_fit.py b/tests/IVIMmodels/unit_tests/test_ivim_fit.py index 41a08af..39f2cb8 100644 --- a/tests/IVIMmodels/unit_tests/test_ivim_fit.py +++ b/tests/IVIMmodels/unit_tests/test_ivim_fit.py @@ -167,8 +167,9 @@ def test_ivim_fit_saved(name, bvals, data, algorithm, xfail, kwargs, tolerances, fit = OsipiBase(algorithm=algorithm, **kwargs) signal, ratio = signal_helper(data["data"]) tolerances = tolerances_helper(tolerances, ratio, data["noise"]) - [f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals) - npt.assert_allclose(data['f'], f_fit, rtol=tolerances["rtol"]["f"], atol=tolerances["atol"]["f"]) - npt.assert_allclose(data['D'], D_fit, rtol=tolerances["rtol"]["D"], atol=tolerances["atol"]["D"]) - npt.assert_allclose(data['Dp'], Dp_fit, rtol=tolerances["rtol"]["Dp"], atol=tolerances["atol"]["Dp"]) + #[f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals) + fit = fit.osipi_fit(signal, bvals) + npt.assert_allclose(data['f'], fit['f'], rtol=tolerances["rtol"]["f"], atol=tolerances["atol"]["f"]) + npt.assert_allclose(data['D'], fit['D'], rtol=tolerances["rtol"]["D"], atol=tolerances["atol"]["D"]) + npt.assert_allclose(data['Dp'], fit['D*'], rtol=tolerances["rtol"]["Dp"], atol=tolerances["atol"]["Dp"])