diff --git a/core/functions/test_hdf.py b/core/functions/test_hdf.py index 683c005b2f319afb7f3d1f20c9969b6c49dd4a59..5bd3b1a12e9b08743b8058d17ef4bb23719ac6a4 100644 --- a/core/functions/test_hdf.py +++ b/core/functions/test_hdf.py @@ -5,11 +5,15 @@ from core.cells import Cells import pandas as pd # f = h5py.File("/home/alan/Documents/sync_docs/PhD/tmp/DO6MS2_003store.h5") -f = h5py.File( - "/shared_libs/pipeline-core/scripts/data/20191026_ss_experiments_01/DO6MS2_003store.h5" -) +fname = "/shared_libs/pipeline-core/scripts/data/20191026_ss_experiments_01/DO6MS2_003store.h5" +f = h5py.File(fname) tracks = f["/extraction/general/None/area"][()] -cell = Cells.from_source("/home/alan/Documents/sync_docs/PhD/tmp/DO6MS2_003store.h5") +cell = Cells.from_source(fname) from postprocessor.core.processes.picker import Picker, PickerParameters picker = Picker(cells=cell, parameters=PickerParameters.default()) + +from postprocessor.core.processor import PostProcessor, PostProParameters + +pp = PostProcessor(filename=fname, parameters=PostProParameters.default()) +pp.run() diff --git a/core/functions/tracks.py b/core/functions/tracks.py index ecc4e0f56742b720ffb01e7d9ac79c325983a72f..2895b6764aa8a72749c36e35251e3864c87df066 100644 --- a/core/functions/tracks.py +++ b/core/functions/tracks.py @@ -47,7 +47,7 @@ def max_nonstop_ntps(track: pd.Series) -> int: def get_tracks_ntps(tracks: pd.DataFrame) -> pd.Series: - return tracks.apply(get_ntps, axis=1) + return tracks.apply(max_ntps, axis=1) def get_avg_gr(track: pd.Series) -> int: @@ -56,7 +56,7 @@ def get_avg_gr(track: pd.Series) -> int: :param tracks: Series with volume and timepoints as indices """ - ntps = get_ntps(track) + ntps = max_ntps(track) vals = track.dropna().values gr = (vals[-1] - vals[0]) / ntps return gr @@ -138,6 +138,9 @@ def get_joint_ids(merging_seqs) -> dict: output {a:a, b:a, c:a, d:a} """ + if not merging_seqs: + return {} + targets, origins = list(zip(*merging_seqs)) static_tracks = set(targets).difference(origins) diff --git a/core/io/signal.py b/core/io/signal.py index 5f3e8c6d80158824662e30b84be633b413dca9e6..91907bd5cccc1bedaf9eb9c51faa6ee17e7a322c 100644 --- a/core/io/signal.py +++ b/core/io/signal.py @@ -1,3 +1,5 @@ +import pandas as pd + from postprocessor.core.io.base import BridgeH5 @@ -12,7 +14,7 @@ class Signal(BridgeH5): def __getitem__(self, dataset): dset = self._hdf[dataset][()] attrs = self._hdf[dataset].attrs - first_dataset = "/" + dataset.split("/")[0] + "/" + first_dataset = dataset.split("/")[1] + "/" timepoints = self._hdf[first_dataset].attrs["processed_timepoints"] if "cell_label" in self._hdf[dataset].attrs: diff --git a/core/processes/merger.py b/core/processes/merger.py index 5c27dd0ba8552ba9fa6b743fc51f5ae609515a0b..51a91bf98e22e2c9e4c82bac10830494a839b513 100644 --- a/core/processes/merger.py +++ b/core/processes/merger.py @@ -1,4 +1,8 @@ -class MergerParameters: +from postprocessor.core.processes.base import ParametersABC, ProcessABC +from postprocessor.core.functions.tracks import clean_tracks, merge_tracks, join_tracks + + +class MergerParameters(ParametersABC): """ :param tol: float or int threshold of average (prediction error/std) necessary to consider two tracks the same. If float is fraction of first track, @@ -26,14 +30,26 @@ class MergerParameters: self.min_avg_delta = min_avg_delta + @classmethod + def default(cls): + return cls.from_dict( + { + "smooth": False, + "tolerance": 0.1, + "window": 5, + "degree": 3, + "min_avg_delta": 0.9, + } + ) + -class Merger: +class Merger(ProcessABC): """ TODO Integrate functions/tracks.py inside this class? """ def __init__(self, parameters): - self.parameters = parameters + super().__init__(parameters) def run(self, signal): - merged, joint_pairs = merge_tracks(signal, min_len=self.window + 1) + merged, joint_pairs = merge_tracks(signal) # , min_len=self.window + 1) diff --git a/core/processes/picker.py b/core/processes/picker.py index 9342ff17a95ced03b007cdc20cb75fccb27a268b..367a88a1ad1a9e6145301f5f265aa48b0394d4ca 100644 --- a/core/processes/picker.py +++ b/core/processes/picker.py @@ -88,7 +88,7 @@ class Picker(ProcessABC): return signals.loc[idx] def pick_by_condition(self, signals): - idx = switch_case(self.condition[0], signals, self.condition[1]) + idx = self.switch_case(self.condition[0], signals, self.condition[1]) return signals.loc[idx] def run(self, signals): @@ -96,22 +96,23 @@ class Picker(ProcessABC): self.signals = getattr(self, "pick_by_" + alg)(signals) return self.signals + @staticmethod + def switch_case( + condition: str, + signals: pd.DataFrame, + threshold: Union[float, int], + ): + threshold_asint = _as_int(threshold, signals.shape[1]) + case_mgr = { + "present": signals.apply(max_ntps, axis=1) > threshold_asint, + "nonstoply_present": signals.apply(max_nonstop_ntps, axis=1) + > threshold_asint, + "quantile": [np.quantile(signals.values[signals.notna()], threshold)], + } + return case_mgr[condition] + -def as_int(threshold: Union[float, int], ntps: int): +def _as_int(threshold: Union[float, int], ntps: int): if type(threshold) is float: threshold = threshold / ntps return threshold - - -def switch_case( - condition: str, - signals: pd.DataFrame, - threshold: Union[float, int], -): - threshold_asint = as_int(threshold, signals.shape[1]) - case_mgr = { - "present": signals.apply(max_ntps, axis=1) > threshold_asint, - "nonstoply_present": signals.apply(max_nonstop_ntps, axis=1) > threshold_asint, - "quantile": [np.quantile(signals.values[signals.notna()], threshold)], - } - return case_mgr[condition] diff --git a/core/processor.py b/core/processor.py index 9e433076c1ba64bdf1a96fe9ea51d1bf67c65cb6..41e3c63041316055d908f540c4e313c936e80bc7 100644 --- a/core/processor.py +++ b/core/processor.py @@ -42,25 +42,34 @@ class PostProParameters(ParametersABC): }, ) + def to_dict(self): + return {k: _if_dict(v) for k, v in self.__dict__()} + class PostProcessor: - def __init__(self, fname, parameters, signals): + def __init__(self, filename, parameters): self.parameters = parameters - self._signals = Signals(fname) - self._writer = Writer(fname) + self._signals = Signal(filename) + self._writer = Writer(filename) self.datasets = parameters["datasets"] self.merger = Merger(parameters["merger"]) self.picker = Picker( - parameters=parameters["picker"], cell=Cells.from_source(fname) + parameters=parameters["picker"], cells=Cells.from_source(filename) ) self.processes = [ self.get_process(process) for process in parameters["processes"] ] def run(self): - self.merger.run(signals[self.datasets["merger"]]) - self.picker.run(signals[self.datasets["picker"]]) + self.merger.run(self._signals[self.datasets["merger"]]) + self.picker.run(self._signals[self.datasets["picker"]]) for process, dataset in zip(self.processes, self.datasets["processes"]): - process_result = process.run(signals.get_dataset(dataset)) + process_result = process.run(self._signals.get_dataset(dataset)) self.writer.write(process_result, dataset) + + +def _ifdict(item): + if hasattr(item, "to_dict"): + item = item.to_dict() + return item