diff --git a/core/functions/tracks.py b/core/functions/tracks.py index bff48680a3ab1f46f656865270fdcc6d5e7722ee..ecc4e0f56742b720ffb01e7d9ac79c325983a72f 100644 --- a/core/functions/tracks.py +++ b/core/functions/tracks.py @@ -214,13 +214,12 @@ def get_joinable(tracks, smooth=False, tol=0.1, window=5, degree=3) -> dict: """ tracks.index.names = [ - "pos", "trap", "cell", ] # TODO remove this once it is integrated in the tracker # contig=tracks.groupby(['pos','trap']).apply(tracks2contig) clean = clean_tracks(tracks, min_len=window + 1, min_gr=0.9) # get useful tracks - contig = clean.groupby(["pos", "trap"]).apply(get_contiguous_pairs) + contig = clean.groupby(["trap"]).apply(get_contiguous_pairs) contig = contig.loc[contig.apply(len) > 0] # candict = {k:v for d in contig.values for k,v in d.items()} diff --git a/core/merger.py b/core/merger.py index 6000a2b5467629e0d9a3097db60a3262e1d48f17..7659a6807cb3183ea02852df59f1a979639b7ecc 100644 --- a/core/merger.py +++ b/core/merger.py @@ -1,5 +1,36 @@ -# Classes in charge of merging tracks +class Parameters: + """ + :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, + if int it is absolute units. + :param window: int value of window used for savgol_filter + :param degree: int value of polynomial degree passed to savgol_filter + """ -class Merger(BasePicker): + def __init__( + self, + smooth: bool = False, + tolerance: float = 0.1, + window: int = 5, + degree: int = 3, + min_avg_delta: float = 0.9, + ): + + self.smooth = smooth + + self.tolerance = tolerance + + self.window = window + + self.degree = degree + + self.min_avg_delta = min_avg_delta + + +class Merger: + # Class in charge of merging tracks def __init__(self, parameters): - pass + self.parameters = parameters + + def run(self, signal): + merged, joint_pairs = merge_tracks(signal, min_len=self.window + 1) diff --git a/core/picker.py b/core/picker.py index 0a4ed16652354581ca4c59a31c8517e50220c135..2618ae99a99b307918eebec00a1c8fb4b10dc728 100644 --- a/core/picker.py +++ b/core/picker.py @@ -1,11 +1,10 @@ -# from abc import ABC, abstractmethod from typing import Tuple, Union, List import numpy as np import pandas as pd from core.cells import CellsHDF -from postprocessor.core.functions.tracks import max_ntps, max_nonstop_ntps +from postprocessor.core.functions.signals import max_ntps, max_nonstop_ntps # def BasePicker(ABC): @@ -18,24 +17,24 @@ from postprocessor.core.functions.tracks import max_ntps, max_nonstop_ntps class Picker: """ - :tracks: pd.DataFrame + :signals: pd.DataFrame of data used for selection, such as area or GFP/np.max/mean :cells: Cell object passed to the constructor :condition: Tuple with condition and associated parameter(s), conditions can be "present", "nonstoply_present" or "quantile". - Determines the thersholds or fractions of tracks/signals to use. - :lineage: str {"mothers", "daughters", "families", "orphans"}. Mothers/daughters picks cells with those tags, families pick the union of both and orphans the difference between the total and families. + Determines the thersholds or fractions of signals/signals to use. + :lineage: str {"mothers", "daughters", "families" (mothers AND daughters), "orphans"}. Mothers/daughters picks cells with those tags, families pick the union of both and orphans the difference between the total and families. """ def __init__( self, - tracks: pd.DataFrame, + signals: pd.DataFrame, cells: CellsHDF, condition: Tuple[str, Union[float, int]] = None, lineage: str = None, sequence: List[str] = ["lineage", "condition"], ): - self.tracks = tracks - self._index = tracks.index + self.signals = signals + self._index = signals.index self._cells = cells self.condition = condition self.lineage = lineage @@ -57,7 +56,7 @@ class Picker: return mb_matrix def pick_by_lineage(self): - idx = self.tracks.index + idx = self.signals.index if self.lineage: ma = self._cells["mother_assign"] @@ -75,18 +74,18 @@ class Picker: idx = idx[list(set(range(len(idx))).difference(families))] idx = self._index[idx] - idx = list(set(idx).intersection(self.tracks.index)) + idx = list(set(idx).intersection(self.signals.index)) - return self.tracks.loc[idx] + return self.signals.loc[idx] def pick_by_condition(self): - idx = switch_case(self.condition[0], self.tracks, self.condition[1]) - return self.tracks.loc[idx] + idx = switch_case(self.condition[0], self.signals, self.condition[1]) + return self.signals.loc[idx] def run(self): for alg in self.sequence: - self.tracks = getattr(self, "pick_by" + alg)() - return self.tracks + self.signals = getattr(self, "pick_by" + alg)() + return self.signals def as_int(threshold: Union[float, int], ntps: int): @@ -97,13 +96,13 @@ def as_int(threshold: Union[float, int], ntps: int): def switch_case( condition: str, - tracks: pd.DataFrame, + signals: pd.DataFrame, threshold: Union[float, int], ): - threshold_asint = as_int(threshold, tracks.shape[1]) + threshold_asint = as_int(threshold, signals.shape[1]) case_mgr = { - "present": tracks.apply(max_ntps, axis=1) > threshold_asint, - "nonstoply_present": tracks.apply(max_nonstop_ntps, axis=1) > threshold_asint, - "quantile": [np.quantile(tracks.values[tracks.notna()], threshold)], + "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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..917b951773a11d0b4d591be8951993382f824bbb 100644 --- a/core/processor.py +++ b/core/processor.py @@ -0,0 +1,13 @@ +class PostProcessor: + def __init__(self, parameters): + self.parameters = parameters + + self.merger = Merger(parameters["merger"]) + self.picker = Picker(parameters["picker"]) + self.processes, self.branches = [ + (self.get_process(process), branch) + for process, branch in parameters["processes"] + ] + + def run(self): + pass