"""
Module with functions to read and write fit results.
"""
import pathlib
from typing import Dict, List, Tuple, Union
import numpy as np
import scipy.io as sio
# TODO: likely not going to do .csv, .mat files seem great kind of hdf5 file format
# TODO: add function to save fit results to .csv file (also as pickle file??)
# TODO: add function to read fit results to .csv file
# TODO: add function to read and write fit results from .mat file from original GRID
# package
[docs]def write_fit_results(
path: Union[str, pathlib.Path],
fit_results: Dict[str, Dict[str, Union[np.ndarray, float]]],
) -> None:
"""
Write fit results to a .mat file.
Parameters
----------
path : Union[str, pathlib.Path]
The file path to write the fit results to.
fit_results : Dict[str, Dict[str, Union[np.ndarray, float]]]
A dictionary containing the fit results, where the keys are the names of the fit
results and the values are dictionaries. The dictionaries have keys 'grid' or
f"{n}-exp", where n is the number of exponentials, and values that are either
numpy arrays or floats.
Returns
-------
None
Examples
--------
>>> fit_results = {'grid': {'k': np.array([1.0e-03, ..., 10.0]),
... 's': np.array([1.000e-03, ..., 2.500e-03]), 'a': 0.022, 'loss': 0.0047},
... '1-exp': {'k': array([0.02563639]), 's': array([1.]), 'a': 0.08514936433699753,
... 'loss': 1.2825570522448484}}
>>> write_fit_results("/path/to/file.mat", fit_results)
Fit results are saved in "/path/to/file.mat".
"""
if isinstance(path, str):
path = pathlib.Path(path)
sio.savemat(path, fit_results)
print(f"Fit results are saved in {path}")
[docs]def read_fit_results(
path: Union[str, pathlib.Path]
) -> Dict[str, Dict[str, Union[np.ndarray, float]]]:
"""
Function reads the fit results from a .mat file.
Parameters
----------
path : Union[str, pathlib.Path]
The file path to read the fit results from.
Returns
-------
fit_results : Dict[str, Dict[str, Union[np.ndarray, float]]]
A dictionary containing the fit results, where the keys are the names of the fit results and the values are
dictionaries. The dictionaries have keys 'grid' or f"{n}-exp", where n is the number of exponentials, and
values that are either numpy arrays or floats.
Examples
--------
>>> fit_results = read_fit_results("/path/to/file.mat")
>>> {'grid': {'k': np.array([1.0e-03, ..., 10.0]), 's': np.array([1.000e-03, ..., 2.500e-03]), 'a': 0.022, 'loss': 0.0047}, '1-exp': {'k': array([0.02563639]), 's': array([1.]), 'a': 0.08514936433699753, 'loss': 1.2825570522448484}}
"""
mat_contents = sio.loadmat(path, simplify_cells=True)
return mat_contents
[docs]def read_fit_results_trackit(
path: Union[str, pathlib.Path]
) -> Dict[str, Dict[str, Union[np.ndarray, float]]]:
"""Function loads and parses the fit results from the original GRID package,
which saves the data in a different data structure, so hence there is some extra
parsing involved."""
# TODO: improve documentation
if isinstance(path, str):
path = pathlib.Path(path)
mat_contents = sio.loadmat(path, simplify_cells=True)
# Create the dictionary with the spectrum values
fit_results_grid = dict()
fit_results_grid["k"] = mat_contents["spectrum"]["dissociation_rates"]
fit_results_grid["S"] = mat_contents["spectrum"]["Spectrum"]
fit_results_grid["a1"] = mat_contents["spectrum"]["bleachingnumber_1"]
fit_results_grid["error_test"] = mat_contents["spectrum"]["error_test"]
# Single-exponential data
fit_results_single_exp = dict()
fit_results_single_exp["k"] = np.array(
[mat_contents["monoexponential"]["dissociation_rate"]], dtype=np.float64
)
fit_results_single_exp["s"] = np.ones(1, dtype=np.float64)
fit_results_single_exp["a"] = mat_contents["monoexponential"]["bleachingnumber_1"]
fit_results_single_exp["adj_R_squared"] = mat_contents["monoexponential"][
"Adj_R_Sqared"
]
# Double-exponential data
fit_results_double_exp = dict()
fit_results_double_exp["k"] = mat_contents["twoexponential"]["dissociation_rates"]
fit_results_double_exp["s"] = mat_contents["twoexponential"]["Amplitudes"]
fit_results_double_exp["a"] = mat_contents["twoexponential"]["bleachingnumber_1"]
fit_results_double_exp["adj_R_squared"] = mat_contents["twoexponential"][
"Adj_R_Sqared"
]
# Triple-exponential data
fit_results_triple_exp = dict()
fit_results_triple_exp["k"] = mat_contents["threeexponential"]["dissociation_rates"]
fit_results_triple_exp["s"] = mat_contents["threeexponential"]["Amplitudes"]
fit_results_triple_exp["a"] = mat_contents["threeexponential"]["bleachingnumber_1"]
fit_results_triple_exp["adj_R_squared"] = mat_contents["threeexponential"][
"Adj_R_Sqared"
]
# Throw all the results into one dictionary
fit_results = {
"grid": fit_results_grid,
"1-exp": fit_results_single_exp,
"2-exp": fit_results_double_exp,
"3-exp": fit_results_triple_exp,
}
return fit_results
[docs]def write_data_grid_resampling(
path: Union[str, pathlib.Path],
fit_result_full: Tuple[Dict[str, Union[np.ndarray, float]]],
fit_results_resampled: List[Dict[str, Union[np.ndarray, float]]],
):
"""Function writes the resampling results to a .mat file."""
if isinstance(path, str):
path = pathlib.Path(path)
data_save = {"result100": fit_result_full, "results": fit_results_resampled}
sio.savemat(path, data_save)
print(f"Resampling results are saved in {path}")
[docs]def read_data_grid_resampling(
path: str,
) -> Tuple[
Dict[str, Union[np.ndarray, float]], List[Dict[str, Union[np.ndarray, float]]]
]:
"""Function loads and parses the resampling data from GRID.
Parameters
----------
path: str
Path to the file with all the resampling data
Returns
-------
fit_result_full: Dict[str, Union[np.ndarray, float]]
Dictionary with the following key-value pairs:
"k": np.ndarray with the dissociation rates
"s": np.ndarray with the corresponding weights
"a": bleaching number (a = kb * t_int)
"loss": final cost value
fit_results_resampled: List[Dict[str, Union[np.ndarray, float]]]
List of dictionaries with the following key-value pairs:
"k": np.ndarray with the dissociation rates
"s": np.ndarray with the corresponding weights
"a": bleaching number (a = kb * t_int)
"loss": final cost value
Every dictionary entry in the list contains the results of one data resample.
"""
mat_contents = sio.loadmat(path, simplify_cells=True)
fit_result_full = mat_contents["result100"]
fit_results_resampled = mat_contents["results"]
return fit_result_full, fit_results_resampled
[docs]def read_data_grid_resampling_trackit(
path: str,
) -> Tuple[
Dict[str, Union[np.ndarray, float]], List[Dict[str, Union[np.ndarray, float]]]
]:
"""Function loads and parses the resampling data from GRID.
Parameters
----------
path: str
Path to the file with all the resampling data
Returns
-------
fit_result_full: Dict[str, Union[np.ndarray, float]]
Dictionary with the following key-value pairs:
"k": np.ndarray with the dissociation rates
"s": np.ndarray with the corresponding weights
"a": bleaching number (a = kb * t_int)
"loss": np.NaN (since this is not provided by TrackIt resampling data)
fit_results_resampled: List[Dict[str, Union[np.ndarray, float]]]
List of dictionaries with the following key-value pairs:
"k": np.ndarray with the dissociation rates
"s": np.ndarray with the corresponding weights
"a": bleaching number (a = kb * t_int)
"loss": np.NaN (since this is not provided by TrackIt resampling data)
Every dictionary entry in the list contains the results of one data resample.
"""
mat_contents = sio.loadmat(path, simplify_cells=True)
fit_result_full_temp = mat_contents["result100"]
fit_results_resampled_temp = mat_contents["results"]
# Update the keys, so consistent with API requirements
fit_result_full = {
"grid": {
"k": fit_result_full_temp["k"],
"s": fit_result_full_temp["S"],
"a": fit_result_full_temp["a1"],
"loss": np.NaN,
}
}
fit_results_resampled = list()
for fit_result_resampled_temp in fit_results_resampled_temp:
# Update the keys, so consistent with API requirements
result_resampled = {
"grid": {
"k": fit_result_resampled_temp["k"],
"s": fit_result_resampled_temp["S"],
"a": fit_result_resampled_temp["a1"],
"loss": np.NaN,
}
}
fit_results_resampled.append(result_resampled)
return fit_result_full, fit_results_resampled