From 9a7ec445e3649a172c8109a8c6ccf46bf000ee0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Al=C3=A1n=20Mu=C3=B1oz?= <amuoz@ed.ac.uk>
Date: Mon, 18 Oct 2021 14:08:37 +0100
Subject: [PATCH] rename postprocessor

Former-commit-id: 184f18f71b1d0ce12f1ef25e4a3a9b040f3cd828
---
 __init__.py                                   |   0
 examples/basic_processes.py                   |  15 -
 examples/group.py                             |  21 -
 examples/testing.py                           | 220 ---------
 .../extraction/joinable_pairs.gz              | Bin 15488 -> 0 bytes
 .../extraction/phl_ura8_024.gz.REMOVED.git-id |   1 -
 postprocessor/__init__.py                     |   1 -
 .../__pycache__/processor.cpython-37.pyc      | Bin 9941 -> 0 bytes
 postprocessor/functions/__init__.py           |   1 -
 .../__pycache__/tracks.cpython-37.pyc         | Bin 13256 -> 0 bytes
 postprocessor/functions/test_hdf.py           |  19 -
 postprocessor/functions/tracks.py             | 429 ------------------
 postprocessor/group.py                        | 130 ------
 postprocessor/old/cell.py                     |  23 -
 postprocessor/old/ph.py                       | 364 ---------------
 postprocessor/old/postprocessor.py            | 106 -----
 postprocessor/processes/__init__.py           |   1 -
 .../__pycache__/aggregate.cpython-37.pyc      | Bin 2881 -> 0 bytes
 .../processes/__pycache__/base.cpython-37.pyc | Bin 228 -> 0 bytes
 .../__pycache__/bud_metric.cpython-37.pyc     | Bin 2986 -> 0 bytes
 .../__pycache__/davol.cpython-37.pyc          | Bin 2827 -> 0 bytes
 .../__pycache__/dsignal.cpython-37.pyc        | Bin 1462 -> 0 bytes
 .../__pycache__/merger.cpython-37.pyc         | Bin 1940 -> 0 bytes
 .../__pycache__/picker.cpython-37.pyc         | Bin 11532 -> 0 bytes
 postprocessor/processes/aggregate.py          |  74 ---
 postprocessor/processes/base.py               |   1 -
 postprocessor/processes/births.py             |   1 -
 postprocessor/processes/bud_metric.py         |  65 ---
 postprocessor/processes/dsignal.py            |  28 --
 postprocessor/processes/gpsignal.py           |  97 ----
 postprocessor/processes/merger.py             |  60 ---
 postprocessor/processes/peaks.py              |  44 --
 postprocessor/processes/picker.py             | 339 --------------
 postprocessor/processes/savgol.py             | 152 -------
 postprocessor/processes/template.py           |  31 --
 postprocessor/processor.py                    | 316 -------------
 36 files changed, 2539 deletions(-)
 delete mode 100644 __init__.py
 delete mode 100644 examples/basic_processes.py
 delete mode 100644 examples/group.py
 delete mode 100644 examples/testing.py
 delete mode 100644 gluStarv_2_0_x2_dual_phl_ura8_00/extraction/joinable_pairs.gz
 delete mode 100644 gluStarv_2_0_x2_dual_phl_ura8_00/extraction/phl_ura8_024.gz.REMOVED.git-id
 delete mode 100644 postprocessor/__init__.py
 delete mode 100644 postprocessor/__pycache__/processor.cpython-37.pyc
 delete mode 100644 postprocessor/functions/__init__.py
 delete mode 100644 postprocessor/functions/__pycache__/tracks.cpython-37.pyc
 delete mode 100644 postprocessor/functions/test_hdf.py
 delete mode 100644 postprocessor/functions/tracks.py
 delete mode 100644 postprocessor/group.py
 delete mode 100644 postprocessor/old/cell.py
 delete mode 100644 postprocessor/old/ph.py
 delete mode 100644 postprocessor/old/postprocessor.py
 delete mode 100644 postprocessor/processes/__init__.py
 delete mode 100644 postprocessor/processes/__pycache__/aggregate.cpython-37.pyc
 delete mode 100644 postprocessor/processes/__pycache__/base.cpython-37.pyc
 delete mode 100644 postprocessor/processes/__pycache__/bud_metric.cpython-37.pyc
 delete mode 100644 postprocessor/processes/__pycache__/davol.cpython-37.pyc
 delete mode 100644 postprocessor/processes/__pycache__/dsignal.cpython-37.pyc
 delete mode 100644 postprocessor/processes/__pycache__/merger.cpython-37.pyc
 delete mode 100644 postprocessor/processes/__pycache__/picker.cpython-37.pyc
 delete mode 100644 postprocessor/processes/aggregate.py
 delete mode 100644 postprocessor/processes/base.py
 delete mode 100644 postprocessor/processes/births.py
 delete mode 100644 postprocessor/processes/bud_metric.py
 delete mode 100644 postprocessor/processes/dsignal.py
 delete mode 100644 postprocessor/processes/gpsignal.py
 delete mode 100644 postprocessor/processes/merger.py
 delete mode 100644 postprocessor/processes/peaks.py
 delete mode 100644 postprocessor/processes/picker.py
 delete mode 100644 postprocessor/processes/savgol.py
 delete mode 100644 postprocessor/processes/template.py
 delete mode 100644 postprocessor/processor.py

diff --git a/__init__.py b/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/basic_processes.py b/examples/basic_processes.py
deleted file mode 100644
index c322fd4b..00000000
--- a/examples/basic_processes.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from postprocessor.core.processor import PostProcessor, PostProcessorParameters
-
-params = PostProcessorParameters.default()
-pp = PostProcessor(
-    "/shared_libs/pipeline-core/scripts/pH_calibration_dual_phl__ura8__by4741__01/ph_5_29_025store.h5",
-    params,
-)
-tmp = pp.run()
-
-import h5py
-
-# f = h5py.File(
-#     "/shared_libs/pipeline-core/scripts/pH_calibration_dual_phl__ura8__by4741__01/ph_5_29_025store.h5",
-#     "a",
-# )
diff --git a/examples/group.py b/examples/group.py
deleted file mode 100644
index 1e59978a..00000000
--- a/examples/group.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from pathlib import Path
-
-from postprocessor.core.group import GroupParameters, Group
-
-poses = [
-    x.name.split("store")[0]
-    for x in Path(
-        "/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01"
-    ).rglob("*")
-    if x.name != "images.h5"
-]
-
-gr = Group(
-    GroupParameters(
-        signals=["/extraction/general/None/area", "/extraction/mCherry/np_max/median"]
-    )
-)
-gr.run(
-    central_store="/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01",
-    poses=poses,
-)
diff --git a/examples/testing.py b/examples/testing.py
deleted file mode 100644
index d5420d83..00000000
--- a/examples/testing.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from typing import Dict, List, Union
-import re
-
-import numpy as np
-import pandas as pd
-from pandas import Series, DataFrame
-from sklearn.cluster import KMeans
-from matplotlib import pyplot as plt
-import seaborn as sns
-
-import compress_pickle
-
-from postprocessor.core.postprocessor import PostProcessor
-from postprocessor.core.tracks import get_avg_grs, non_uniform_savgol
-from postprocessor.core.tracks import clean_tracks, merge_tracks, join_tracks
-from postprocessor.core.ph import *
-
-sns.set_context("talk", font_scale=1.8)
-sns.set_theme(style="whitegrid")
-
-# pp_c = PostProcessor(source=19920)  # 19916
-# pp_c.load_tiler_cells()
-# # f = "/home/alan/Documents/libs/extraction/extraction/examples/gluStarv_2_0_x2_dual_phl_ura8_00/extraction"
-# # f = "/home/alan/Documents/tmp/pH_calibration_dual_phl__ura8__by4741__01/extraction/"
-# f = "/home/alan/Documents/tmp/pH_calibration_dual_phl__ura8__by4741_Alan4_00/extraction/"
-# pp_c.load_extraction(f)
-
-# calib = process_phs(pp_c)
-# # c = calib.loc[(5.4 < calib["ph"]) & (calib["ph"] < 8)].groupby("ph").mean()
-# sns.violinplot(x="ph", y="em_ratio_mean", data=calib)
-# plt.show()
-
-
-# bring timelapse data and convert it to pH
-
-pp = PostProcessor(source=19831)  # 19831
-pp.load_tiler_cells()
-f = "/home/alan/Documents/tmp/gluStarv_2_0_x2_dual_phl_ura8_00/extraction/"
-# f = "/home/alan/Documents/tmp/downUpshift_2_0_2_glu_dual_phluorin__glt1_psa1_ura7__thrice_00/extraction/"
-pp.load_extraction(f)
-
-import compress_pickle
-
-compress_pickle.dump(pp.extraction, "/home/alan/extraction_example.pkl")
-
-if True:  # Load extracted data or pkld version
-    new_dfs = compress_pickle.load("/home/alan/Documents/tmp/new_dfs.gz")
-# Combine dataframes
-else:
-    new_dfs = combine_dfs(get_dfs(pp))
-    # t = [x.index for x in new_dfs.values()]
-    # i = set(t[0])
-    # for j in t:
-    #     i = i.intersection(j)
-    new_dfs = {
-        k: v
-        for k, v in new_dfs.items()
-        if k[2] != "imBackground"
-        and k[2] != "median"
-        and ~(((k[0] == "GFPFast") | (k[0] == "pHluorin405")) and k[2] == "max2p5pc")
-    }
-
-del pp
-compress_pickle.dump(new_dfs, "/home/alan/Documents/tmp/new_dfs.gz")
-
-
-def get_clean_dfs(dfs=None):
-    if dfs is None:
-        clean_dfs = compress_pickle.load("/home/alan/Documents/tmp/clean_dfs.gz")
-    else:
-
-        # Clean timelapse
-        clean = clean_tracks(new_dfs[("general", None, "area")])
-        tra, joint = merge_tracks(clean)
-        clean_dfs = new_dfs
-        i_ids = set(clean.index).intersection(
-            clean_dfs[("general", None, "area")].index
-        )
-        clean_dfs = {k: v.loc[i_ids] for k, v in clean_dfs.items()}
-        clean_dfs = {k: join_tracks(v, joint, drop=True) for k, v in clean_dfs.items()}
-
-        del new_dfs
-        compress_pickle.dump(clean_dfs, "/home/alan/Documents/tmp/clean_dfs.gz")
-
-
-def plot_ph_hmap(clean_dfs):
-    GFPFast = clean_dfs[("GFPFast", np.maximum, "mean")]
-    phluorin = clean_dfs[("pHluorin405", np.maximum, "mean")]
-    ph = GFPFast / phluorin
-    ph = ph.loc[ph.notna().sum(axis=1) > 0.7 * ph.shape[1]]
-    ph = 1 / ph
-
-    fig, ax = plt.subplots()
-    hmap(ph, cbar_kws={"label": r"emission ratio $\propto$ pH"})
-    plt.xlabel("Time (hours)")
-    plt.ylabel("Cells")
-    xticks = plt.xticks(fontsize=15)[0]
-    ax.set(yticklabels=[], xticklabels=[str(round(i * 5 / 60, 1)) for i in xticks])
-    # plt.setp(ax.get_xticklabels(), Rotation=90)
-    plt.show()
-
-
-def fit_calibs(c, h):
-    h = process_phs(pp_c)
-    h["ratio"] = h["GFPFast_bgsub_median"] / h["pHluorin405_bgsub_median"]
-    sns.lineplot(x="ph", y="ratio", data=h, err_style="bars")
-    plt.show()
-
-    calibs = fit_calibration(c)
-    for k, params in calibs.items():
-        i, j = ("_".join(k.split("_")[:-1]), k.split("_")[-1])
-        if j == "mean" and "k2" not in k:
-            clean_dfs[k] = objective(clean_dfs[i, np.maximum, j], *params)
-
-
-# max 2.5% / med
-def plot_ratio_vs_max2p5(h):
-    fig, ax = plt.subplots()
-    sns.regplot(
-        x="em_ratio_median",
-        y="mCherry_max2p5pc",
-        data=h,
-        scatter=False,
-        ax=ax,
-        color="teal",
-    )
-    sns.scatterplot(x="em_ratio_median", y="max5_d_med", data=h, hue="ph", ax=ax)
-    plt.xlabel(r"Fluorescence ratio $R \propto (1/pH)$")
-    plt.ylabel("Max 2.5% px / median")
-    plt.show()
-
-
-em = clean_dfs[("em_ratio", np.maximum, "mean")]
-area = clean_dfs[("general", None, "area")]
-
-
-def get_grs(clean_dfs):
-    area = clean_dfs[("general", None, "area")]
-    area = area.loc[area.notna().sum(axis=1) > 10]
-    return area.apply(growth_rate, axis=1)
-
-
-def get_agg(dfs, rng):
-    # df dict of DataFrames containing an area/vol one TODO generalise this beyond area
-    # rng tuple of section to use
-    grs = get_grs(dfs)
-    smooth = grs.loc(axis=1)[list(range(rng[0], rng[1]))].dropna(how="all")
-
-    aggregate_mean = lambda dfs, rng: pd.concat(
-        {
-            k[0] + "_" + k[2]: dfs[k].loc[smooth.index, rng[0] : rng[1]].mean(axis=1)
-            for k in clean_dfs.keys()
-        },
-        axis=1,
-    )
-    # f_comp_df = comp_df.loc[(comp_df["gr"] > 0) & (area.notna().sum(axis=1) > 50)]
-
-    agg = aggregate_mean(dfs, rng)
-    agg["max2_med"] = agg["mCherry_max2p5pc"] / agg["mCherry_mean"]
-
-    for c in agg.columns:
-        agg[c + "_log"] = np.log(agg[c])
-
-    agg["gr_mean"] = smooth.loc[set(agg.index).intersection(smooth.index)].mean(axis=1)
-    agg["gr_max"] = smooth.loc[set(agg.index).intersection(smooth.index)].max(axis=1)
-
-    return agg
-
-
-def plot_scatter_fit(x, y, data, hue=None, xlabel=None, ylabel=None, ylim=None):
-    fig, ax = plt.subplots()
-    sns.regplot(x=x, y=y, data=data, scatter=False, ax=ax)
-    sns.scatterplot(x=x, y=y, data=data, ax=ax, alpha=0.1, hue=hue)
-    # plt.show()
-    if xlabel is not None:
-        plt.xlabel(xlabel)
-    if ylabel is not None:
-        plt.ylabel(ylabel)
-    if ylim is not None:
-        plt.ylim(ylim)
-
-    fig.savefig(
-        "/home/alan/Documents/sync_docs/drafts/third_year_pres/figs/"
-        + str(len(data))
-        + "_"
-        + x
-        + "_vs_"
-        + y
-        + ".png",
-        dpi=200,
-    )
-
-
-from extraction.core.argo import Argo, annot_from_dset
-
-
-def additional_feats(aggs):
-    aggs["gr_mean_norm"] = aggs["gr_mean"] * 12
-    aggs["log_ratio_r"] = np.log(1 / aggs["em_ratio_mean"])
-    return aggs
-
-
-def compare_methods_ph_calculation(dfs):
-    GFPFast = dfs[("GFPFast", np.maximum, "mean")]
-    phluorin = dfs[("pHluorin405", np.maximum, "mean")]
-    ph = GFPFast / phluorin
-
-    sns.scatterplot(
-        dfs["em_ratio", np.maximum, "mean"].values.flatten(),
-        ph.values.flatten(),
-        alpha=0.1,
-    )
-    plt.xlabel("ratio_median")
-    plt.ylabel("median_ratio")
-    plt.title("Comparison of ph calculation")
-    plt.show()
-
-
-# get the number of changes in a bool list
-nchanges = lambda x: sum([i for i, j in zip(x[:-2], x[1:]) if operator.xor(i, j)])
diff --git a/gluStarv_2_0_x2_dual_phl_ura8_00/extraction/joinable_pairs.gz b/gluStarv_2_0_x2_dual_phl_ura8_00/extraction/joinable_pairs.gz
deleted file mode 100644
index b0098173a329e2ccb2e036673a4876d41079e49f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 15488
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
MJYhzt+P3Zg04QHatN;K2

diff --git a/gluStarv_2_0_x2_dual_phl_ura8_00/extraction/phl_ura8_024.gz.REMOVED.git-id b/gluStarv_2_0_x2_dual_phl_ura8_00/extraction/phl_ura8_024.gz.REMOVED.git-id
deleted file mode 100644
index 7081c4b1..00000000
--- a/gluStarv_2_0_x2_dual_phl_ura8_00/extraction/phl_ura8_024.gz.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-ea300840eaf24164ad3eae825360b48d437d5575
\ No newline at end of file
diff --git a/postprocessor/__init__.py b/postprocessor/__init__.py
deleted file mode 100644
index e5a0d9b4..00000000
--- a/postprocessor/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python3
diff --git a/postprocessor/__pycache__/processor.cpython-37.pyc b/postprocessor/__pycache__/processor.cpython-37.pyc
deleted file mode 100644
index 748204fad28982f10fc9d95f84a1df10002fbd3e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 9941
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

diff --git a/postprocessor/functions/__init__.py b/postprocessor/functions/__init__.py
deleted file mode 100644
index e5a0d9b4..00000000
--- a/postprocessor/functions/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python3
diff --git a/postprocessor/functions/__pycache__/tracks.cpython-37.pyc b/postprocessor/functions/__pycache__/tracks.cpython-37.pyc
deleted file mode 100644
index e6533c963f5eca8ba6bf984e7b83824829fd3e40..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 13256
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
PsV>aBXU|km)lU2mt5n^$

diff --git a/postprocessor/functions/test_hdf.py b/postprocessor/functions/test_hdf.py
deleted file mode 100644
index 5bd3b1a1..00000000
--- a/postprocessor/functions/test_hdf.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env python3
-# c=CellsHDF.from_source("/home/alan/Documents/sync_docs/PhD/tmp/DO6MS2_003store.h5")
-import h5py
-from core.cells import Cells
-import pandas as pd
-
-# f = h5py.File("/home/alan/Documents/sync_docs/PhD/tmp/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(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/postprocessor/functions/tracks.py b/postprocessor/functions/tracks.py
deleted file mode 100644
index e47e6023..00000000
--- a/postprocessor/functions/tracks.py
+++ /dev/null
@@ -1,429 +0,0 @@
-"""
-Functions to process, filter and merge tracks.
-"""
-
-# from collections import Counter
-
-from copy import copy
-from typing import Union, List
-
-import numpy as np
-import pandas as pd
-from utils_find_1st import find_1st, cmp_larger
-
-import more_itertools as mit
-from scipy.signal import savgol_filter
-
-# from scipy.optimize import linear_sum_assignment
-# from scipy.optimize import curve_fit
-
-from matplotlib import pyplot as plt
-
-
-def load_test_dset():
-    # Load development dataset to test functions
-    return pd.DataFrame(
-        {
-            ("a", 1, 1): [2, 5, np.nan, 6, 8] + [np.nan] * 5,
-            ("a", 1, 2): list(range(2, 12)),
-            ("a", 1, 3): [np.nan] * 8 + [6, 7],
-            ("a", 1, 4): [np.nan] * 5 + [9, 12, 10, 14, 18],
-        },
-        index=range(1, 11),
-    ).T
-
-
-def max_ntps(track: pd.Series) -> int:
-    # Get number of timepoints
-    indices = np.where(track.notna())
-    return np.max(indices) - np.min(indices)
-
-
-def max_nonstop_ntps(track: pd.Series) -> int:
-    nona_tracks = track.notna()
-    consecutive_nonas_grouped = [
-        len(list(x)) for x in mit.consecutive_groups(np.flatnonzero(nona_tracks))
-    ]
-    return max(consecutive_nonas_grouped)
-
-
-def get_tracks_ntps(tracks: pd.DataFrame) -> pd.Series:
-    return tracks.apply(max_ntps, axis=1)
-
-
-def get_avg_gr(track: pd.Series) -> int:
-    """
-    Get average growth rate for a track.
-
-    :param tracks: Series with volume and timepoints as indices
-    """
-    ntps = max_ntps(track)
-    vals = track.dropna().values
-    gr = (vals[-1] - vals[0]) / ntps
-    return gr
-
-
-def get_avg_grs(tracks: pd.DataFrame) -> pd.DataFrame:
-    """
-    Get average growth rate for a group of tracks
-
-    :param tracks: (m x n) dataframe where rows are cell tracks and
-        columns are timepoints
-    """
-    return tracks.apply(get_avg_gr, axis=1)
-
-
-def clean_tracks(tracks, min_len: int = 15, min_gr: float = 1.0) -> pd.DataFrame:
-    """
-    Clean small non-growing tracks and return the reduced dataframe
-
-    :param tracks: (m x n) dataframe where rows are cell tracks and
-        columns are timepoints
-    :param min_len: int number of timepoints cells must have not to be removed
-    :param min_gr: float Minimum mean growth rate to assume an outline is growing
-    """
-    ntps = get_tracks_ntps(tracks)
-    grs = get_avg_grs(tracks)
-
-    growing_long_tracks = tracks.loc[(ntps >= min_len) & (grs > min_gr)]
-
-    return growing_long_tracks
-
-
-def merge_tracks(tracks, drop=False, **kwargs) -> pd.DataFrame:
-    """
-    Join tracks that are contiguous and within a volume threshold of each other
-
-    :param tracks: (m x n) dataframe where rows are cell tracks and
-        columns are timepoints
-    :param kwargs: args passed to get_joinable
-
-    returns
-
-    :joint_tracks: (m x n) Dataframe where rows are cell tracks and
-        columns are timepoints. Merged tracks are still present but filled
-        with np.nans.
-    """
-
-    # calculate tracks that can be merged until no more traps can be merged
-    joinable_pairs = get_joinable(tracks, **kwargs)
-    if joinable_pairs:
-        tracks = join_tracks(tracks, joinable_pairs, drop=drop)
-    joint_ids = get_joint_ids(joinable_pairs)
-
-    return (tracks, joinable_pairs)
-
-
-def get_joint_ids(merging_seqs) -> dict:
-    """
-    Convert a series of merges into a dictionary where
-    the key is the cell_id of destination and the value a list
-    of the other track ids that were merged into the key
-
-    :param merging_seqs: list of tuples of indices indicating the
-    sequence of merging events. It is important for this to be in sequential order
-
-    How it works:
-
-    The order of merging matters for naming, always the leftmost track will keep the id
-
-    For example, having tracks (a, b, c, d) and the iterations of merge events:
-
-    0 a b c d
-    1 a b cd
-    2 ab cd
-    3 abcd
-
-    We shold get:
-
-    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)
-
-    joint = {track_id: track_id for track_id in static_tracks}
-    for target, origin in merging_seqs:
-        joint[origin] = target
-
-    moved_target = [
-        k for k, v in joint.items() if joint[v] != v and v in joint.values()
-    ]
-
-    for orig in moved_target:
-        joint[orig] = rec_bottom(joint, orig)
-
-    return {
-        k: v for k, v in joint.items() if k != v
-    }  # remove ids that point to themselves
-
-
-def rec_bottom(d, k):
-    if d[k] == k:
-        return k
-    else:
-        return rec_bottom(d, d[k])
-
-
-def join_tracks(tracks, joinable_pairs, drop=True) -> pd.DataFrame:
-    """
-    Join pairs of tracks from later tps towards the start.
-
-    :param tracks: (m x n) dataframe where rows are cell tracks and
-        columns are timepoints
-
-    returns (copy)
-
-    :param joint_tracks: (m x n) Dataframe where rows are cell tracks and
-        columns are timepoints. Merged tracks are still present but filled
-        with np.nans.
-    :param drop: bool indicating whether or not to drop moved rows
-
-    """
-
-    tmp = copy(tracks)
-    for target, source in joinable_pairs:
-        tmp.loc[target] = join_track_pair(tmp.loc[target], tmp.loc[source])
-
-        if drop:
-            tmp = tmp.drop(source)
-
-    return tmp
-
-
-def join_track_pair(target, source):
-    tgt_copy = copy(target)
-    end = find_1st(target.values[::-1], 0, cmp_larger)
-    tgt_copy.iloc[-end:] = source.iloc[-end:].values
-    return tgt_copy
-
-
-def get_joinable(tracks, smooth=False, tol=0.1, window=5, degree=3) -> dict:
-    """
-    Get the pair of track (without repeats) that have a smaller error than the
-    tolerance. If there is a track that can be assigned to two or more other
-    ones, it chooses the one with a lowest error.
-
-    :param tracks: (m x n) dataframe where rows are cell tracks and
-        columns are timepoints
-    :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
-
-    """
-
-    # Commented because we are not smoothing in this step yet
-    # candict = {k:v for d in contig.values for k,v in d.items()}
-
-    # smooth all relevant tracks
-
-    if smooth:  # Apply savgol filter TODO fix nans affecting edge placing
-        clean = clean_tracks(
-            tracks, min_len=window + 1, min_gr=0.9
-        )  # get useful tracks
-        savgol_on_srs = lambda x: non_uniform_savgol(x.index, x.values, window, degree)
-        contig = clean.groupby(["trap"]).apply(get_contiguous_pairs)
-        contig = contig.loc[contig.apply(len) > 0]
-        flat = set([k for v in contig.values for i in v for j in i for k in j])
-        smoothed_tracks = clean.loc[flat].apply(savgol_on_srs, 1)
-    else:
-        contig = tracks.groupby(["trap"]).apply(get_contiguous_pairs)
-        contig = contig.loc[contig.apply(len) > 0]
-        flat = set([k for v in contig.values for i in v for j in i for k in j])
-        smoothed_tracks = tracks.loc[flat].apply(lambda x: np.array(x.values), axis=1)
-
-    # fetch edges from ids TODO (IF necessary, here we can compare growth rates)
-    idx_to_edge = lambda preposts: [
-        (
-            [get_val(smoothed_tracks.loc[pre], -1) for pre in pres],
-            [get_val(smoothed_tracks.loc[post], 0) for post in posts],
-        )
-        for pres, posts in preposts
-    ]
-    edges = contig.apply(idx_to_edge)
-
-    closest_pairs = edges.apply(get_vec_closest_pairs, tol=tol)
-
-    # match local with global ids
-    joinable_ids = [
-        localid_to_idx(closest_pairs.loc[i], contig.loc[i]) for i in closest_pairs.index
-    ]
-
-    return [pair for pairset in joinable_ids for pair in pairset]
-
-
-get_val = lambda x, n: x[~np.isnan(x)][n] if len(x[~np.isnan(x)]) else np.nan
-
-
-def localid_to_idx(local_ids, contig_trap):
-    """Fetch then original ids from a nested list with joinable local_ids
-
-    input
-    :param local_ids: list of list of pairs with cell ids to be joint
-    :param local_ids: list of list of pairs with corresponding cell ids
-
-    return
-    list of pairs with (experiment-level) ids to be joint
-    """
-    lin_pairs = []
-    for i, pairs in enumerate(local_ids):
-        if len(pairs):
-            for left, right in pairs:
-                lin_pairs.append((contig_trap[i][0][left], contig_trap[i][1][right]))
-    return lin_pairs
-
-
-def get_vec_closest_pairs(lst: List, **kwags):
-    return [get_closest_pairs(*l, **kwags) for l in lst]
-
-
-def get_closest_pairs(pre: List[float], post: List[float], tol: Union[float, int] = 1):
-    """Calculate a cost matrix the Hungarian algorithm to pick the best set of
-    options
-
-    input
-    :param pre: list of floats with edges on left
-    :param post: list of floats with edges on right
-    :param tol: int or float if int metrics of tolerance, if float fraction
-
-    returns
-    :: list of indices corresponding to the best solutions for matrices
-
-    """
-    if len(pre) > len(post):
-        dMetric = np.abs(np.subtract.outer(post, pre))
-    else:
-        dMetric = np.abs(np.subtract.outer(pre, post))
-    # dMetric[np.isnan(dMetric)] = tol + 1 + np.nanmax(dMetric) # nans will be filtered
-    # ids = linear_sum_assignment(dMetric)
-    dMetric[np.isnan(dMetric)] = tol + 1 + np.nanmax(dMetric)  # nans will be filtered
-
-    ids = solve_matrix(dMetric)
-    if not len(ids[0]):
-        return []
-
-    norm = (
-        np.array(pre)[ids[len(pre) > len(post)]] if tol < 1 else 1
-    )  # relative or absolute tol
-    result = dMetric[ids] / norm
-    ids = ids if len(pre) < len(post) else ids[::-1]
-
-    return [idx for idx, res in zip(zip(*ids), result) if res <= tol]
-
-
-def solve_matrix(dMetric):
-    """
-    Solve cost matrix focusing on getting the smallest cost at each iteration.
-
-    input
-    :param dMetric: np.array cost matrix
-
-    returns
-    tuple of np.arrays indicating picks with lowest individual value
-    """
-    glob_is = []
-    glob_js = []
-    if (~np.isnan(dMetric)).any():
-        tmp = copy(dMetric)
-        std = sorted(tmp[~np.isnan(tmp)])
-        while (~np.isnan(std)).any():
-            v = std[0]
-            i_s, j_s = np.where(tmp == v)
-            i = i_s[0]
-            j = j_s[0]
-            tmp[i, :] += np.nan
-            tmp[:, j] += np.nan
-            glob_is.append(i)
-            glob_js.append(j)
-
-            std = sorted(tmp[~np.isnan(tmp)])
-
-    return (np.array(glob_is), np.array(glob_js))
-
-
-def plot_joinable(tracks, joinable_pairs, max=64):
-    """
-    Convenience plotting function for debugging and data vis
-    """
-
-    nx = 8
-    ny = 8
-    _, axes = plt.subplots(nx, ny)
-    for i in range(nx):
-        for j in range(ny):
-            if i * ny + j < len(joinable_pairs):
-                ax = axes[i, j]
-                pre, post = joinable_pairs[i * ny + j]
-                pre_srs = tracks.loc[pre].dropna()
-                post_srs = tracks.loc[post].dropna()
-                ax.plot(pre_srs.index, pre_srs.values, "b")
-                # try:
-                #     totrange = np.arange(pre_srs.index[0],post_srs.index[-1])
-                #     ax.plot(totrange, interpolate(pre_srs, totrange), 'r-')
-                # except:
-                #     pass
-                ax.plot(post_srs.index, post_srs.values, "g")
-
-    plt.show()
-
-
-def get_contiguous_pairs(tracks: pd.DataFrame) -> list:
-    """
-    Get all pair of contiguous track ids from a tracks dataframe.
-
-    :param tracks: (m x n) dataframe where rows are cell tracks and
-        columns are timepoints
-    :param min_dgr: float minimum difference in growth rate from the interpolation
-    """
-    # indices = np.where(tracks.notna())
-
-    mins, maxes = [
-        tracks.notna().apply(np.where, axis=1).apply(fn) for fn in (np.min, np.max)
-    ]
-
-    mins_d = mins.groupby(mins).apply(lambda x: x.index.tolist())
-    mins_d.index = mins_d.index - 1  # make indices equal
-    # TODO add support for skipping time points
-    maxes_d = maxes.groupby(maxes).apply(lambda x: x.index.tolist())
-
-    common = sorted(set(mins_d.index).intersection(maxes_d.index), reverse=True)
-
-    return [(maxes_d[t], mins_d[t]) for t in common]
-
-
-# def fit_track(track: pd.Series, obj=None):
-#     if obj is None:
-#         obj = objective
-
-#     x = track.dropna().index
-#     y = track.dropna().values
-#     popt, _ = curve_fit(obj, x, y)
-
-#     return popt
-
-# def interpolate(track, xs) -> list:
-#     '''
-#     Interpolate next timepoint from a track
-
-#     :param track: pd.Series of volume growth over a time period
-#     :param t: int timepoint to interpolate
-#     '''
-#     popt = fit_track(track)
-#     # perr = np.sqrt(np.diag(pcov))
-#     return objective(np.array(xs), *popt)
-
-
-# def objective(x,a,b,c,d) -> float:
-#     # return (a)/(1+b*np.exp(c*x))+d
-#     return (((x+d)*a)/((x+d)+b))+c
-
-# def cand_pairs_to_dict(candidates):
-#     d={x:[] for x,_ in candidates}
-#     for x,y in candidates:
-#         d[x].append(y)
-#     return d
diff --git a/postprocessor/group.py b/postprocessor/group.py
deleted file mode 100644
index 99cf00cf..00000000
--- a/postprocessor/group.py
+++ /dev/null
@@ -1,130 +0,0 @@
-"""
-Class to group multiple positions into one using one of several criteria.
-"""
-
-from pathlib import Path
-import re
-
-import h5py
-import pandas as pd
-
-from postprocessor.core.io.base import groupsort
-from postprocessor.core.io.signal import Signal
-
-from postprocessor.core.processes.base import ParametersABC, ProcessABC
-
-
-class GroupParameters(ParametersABC):
-    def __init__(self, by="name", processes=[], signals=[]):
-        self.by = by
-        self.signals = signals
-        self.processes = processes
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({"by": "name", "signals": [], "processes": []})
-
-
-class Group(ProcessABC):
-    def __init__(self, parameters):
-        super().__init__(parameters)
-
-    def get_position_filenames(self, exp_root, poses):
-        """
-        Get filenames as a dictionary where the key is the position and value the filename.
-        """
-        central_store = Path(exp_root) / "store.h5"
-        if central_store.exists():
-            hdf = h5py.File(central_store, "r")
-            self.filenames = [pos.attrs["filename"] for pos in hdf["/positions/"]]
-            hdf.close()
-        else:  # If no central store just list position files in expt root folder
-            fullfiles = [x for x in Path(exp_root).glob("*store.h5")]
-            files = [x.name for x in Path(exp_root).glob("*store.h5")]
-            filenames = [False for _ in poses]
-            for i, pos in enumerate(poses):
-                matches = [
-                    True if re.match(pos + ".*.h5", fname) else False for fname in files
-                ]
-                if any(matches):
-                    assert sum(matches) == 1, "More than one match"
-                    filenames[i] = (pos, fullfiles[matches.index(True)])
-
-            self.filenames = {fname[0]: fname[1] for fname in filenames if fname}
-
-        self.positions = list(self.filenames.keys())
-        return self.filenames
-
-    def get_signals(self):
-        # hdf = h5py.File(central_store, "r")
-        # keys_d = groupsort(keys)
-        self.signals = {pos: {} for pos in self.filenames.keys()}
-        for pos, fname in self.filenames.items():
-            for signal in self.parameters.signals:
-                self.signals[pos][signal] = pd.read_hdf(fname, signal)
-
-        return self.signals
-
-    def gen_groups(self):
-        if self.by == "group":  # Use group names in metadata
-            pass
-        elif self.by == "name":  # Infer groups from signal concatenation
-            # Remove last four characters and find commonalities larger than 4
-            # characters between posnames and group them that way.
-            groupnames = list(set([x[:-3] for x in self.positions]))
-            self.group_signal_tree = {group: [] for group in groupnames}
-            self.poses_grouped = {group: [] for group in groupnames}
-            for pos in self.positions:
-                group = groupnames[groupnames.index(pos[:-3])]
-                self.group_signal_tree[group].append(self.signals[pos])
-                self.poses_grouped[group].append(pos)
-
-        elif (
-            type(self.by) == tuple
-        ):  # Manually give groups as tuple or list of positions
-            pass
-
-    def concat_signals(self):
-        self.concated_signals = {group: {} for group in self.group_signal_tree}
-        for k, group in self.group_signal_tree.items():
-            for signal in self.parameters.signals:
-                self.concated_signals[k][signal] = pd.concat(
-                    [g[signal] for g in group], keys=self.poses_grouped[k]
-                )
-
-        return self.concated_signals
-
-    def process_signals(self, grouped_signals):
-        pass
-
-    def run(self, central_store, poses):
-
-        self.get_position_filenames(central_store, poses)
-        self.get_signals()
-        self.gen_groups()
-        self.concat_signals()
-        # processed_signals = self.process_signals(grouped_signals)
-
-        return self.concated_signals
-        # return processed_signals
-
-
-poses = [
-    x.name.split("store")[0]
-    for x in Path(
-        "/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01"
-    ).rglob("*")
-    if x.name != "images.h5"
-]
-gr = Group(
-    GroupParameters(
-        signals=["/extraction/general/None/area", "/extraction/mCherry/np_max/median"]
-    )
-)
-gr.run(
-    central_store="/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01",
-    poses=poses,
-)
-signal = Signal(
-    "/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01/ph_5_04_001store.h5"
-)
diff --git a/postprocessor/old/cell.py b/postprocessor/old/cell.py
deleted file mode 100644
index f96f30d5..00000000
--- a/postprocessor/old/cell.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-Main data processing functions. The inputs for this functions must be data series.
-"""
-
-import numpy as np
-from pandas import Series, DataFrames
-
-def bg_sub(data:Series, background:float):
-    return data - background
-
-def ratio(data1:Series, data2:Series):
-    return data1 / data2
-
-def norm_tps(data:Series, tps:list, fun=np.nanmean):
-    return data / fun(data.loc(axis=1)[tps])
-
-def growth_rate(data:Series, alg=None, filt = 'savgol'):
-    if alg is None:
-        alg='standard'
-
-    
-
-    
diff --git a/postprocessor/old/ph.py b/postprocessor/old/ph.py
deleted file mode 100644
index a28a8055..00000000
--- a/postprocessor/old/ph.py
+++ /dev/null
@@ -1,364 +0,0 @@
-from typing import Dict, List, Union
-import re
-
-import numpy as np
-import pandas as pd
-from pandas import Series, DataFrame
-from sklearn.cluster import KMeans
-from matplotlib import pyplot as plt
-import seaborn as sns
-
-import compress_pickle
-
-from postprocessor.core.postprocessor import PostProcessor
-from postprocessor.core.tracks import get_avg_grs, non_uniform_savgol
-from postprocessor.core.ph import *
-
-
-def filter_by_gfp(dfs):
-    gfps = pd.concat([t[("GFPFast_bgsub", np.maximum, "median")] for t in dfs])
-    avgs_gfp = gfps.mean(axis=1)
-    high_gfp = get_high_k2(avgs_gfp)
-    # high_gfp = avgs_gfp[avgs_gfp > 200]
-
-    return high_gfp
-
-
-def filter_by_area(dfs, min=50):
-    areas = pd.concat([t[("general", None, "area")] for t in dfs])
-    avgs_areas = areas[(areas.notna().sum(axis=1) > areas.shape[1] // (1.25))].mean(
-        axis=1
-    )
-    avgs_areas = avgs_areas[(avgs_areas > min)]
-
-    return avgs_areas
-
-
-def get_high_k2(df):
-    kmeans = KMeans(n_clusters=2)
-    vals = df.values.reshape(-1, 1)
-    kmeans.fit(vals)
-    high_clust_id = kmeans.cluster_centers_.argmax()
-
-    return df.loc[kmeans.predict(vals) == high_clust_id]
-
-
-def get_concats(dfs, keys):
-    return pd.concat([t.get((keys), pd.DataFrame()) for t in dfs])
-
-
-def get_dfs(pp):
-    dfs = [pp.extraction[pp.expt.positions[i]] for i in range(len(pp.expt.positions))]
-    return dfs
-
-
-combine_dfs = lambda dfs: {k: get_concats(dfs, k) for k in dfs[0].keys()}
-
-
-def merge_channels(pp, min_area=50):
-    dfs = get_dfs(pp)
-    # rats = get_concats(dfs, ("em_ratio_bgsub", np.maximum, "median"))
-    # gfps = filter_by_gfp(dfs)
-
-    avgs_area = filter_by_area(dfs, min=50)
-    # ids = [x for x in set(gfps.index).intersection(avgs_area.index)]
-    ids = avgs_area.index
-
-    new_dfs = combine_dfs(dfs)
-
-    h = pd.DataFrame(
-        {
-            k[0] + "_" + k[2]: v.loc[ids].mean(axis=1)
-            for k, v in new_dfs.items()
-            if k[-1] != "imBackground"
-        }
-    )
-    return h
-
-
-def process_phs(pp, min_area=200):
-    h = merge_channels(pp, min_area)
-    h.index.names = ["pos", "trap", "cell"]
-    ids = h.index
-    h = h.reset_index()
-
-    h["ph"] = h["pos"].apply(lambda x: float(x[3:7].replace("_", ".")))
-    h["max5_d_med"] = h["mCherry_max2p5pc"] / h["mCherry_median"]
-
-    h = h.set_index(ids)
-    h = h.drop(["pos", "trap", "cell"], axis=1)
-    return h
-
-
-def growth_rate(
-    data: Series, alg=None, filt={"kind": "savgol", "window": 5, "degree": 3}
-):
-    window = filt["window"]
-    degree = filt["degree"]
-    if alg is None:
-        alg = "standard"
-
-    if filt:  # TODO add support for multiple algorithms
-        data = Series(
-            non_uniform_savgol(
-                data.dropna().index, data.dropna().values, window, degree
-            ),
-            index=data.dropna().index,
-        )
-
-    return Series(np.convolve(data, diff_kernel, "same"), index=data.dropna().index)
-
-
-# import numpy as np
-
-# diff_kernel = np.array([1, -1])
-# gr = clean.apply(growth_rate, axis=1)
-# from postprocessor.core.tracks import non_uniform_savgol, clean_tracks
-
-
-def sort_df(df, by="first", rev=True):
-    nona = df.notna()
-    if by == "len":
-        idx = nona.sum(axis=1)
-    elif by == "first":
-        idx = nona.idxmax(axis=1)
-    idx = idx.sort_values().index
-
-    if rev:
-        idx = idx[::-1]
-
-    return df.loc[idx]
-
-
-# test = tmp[("GFPFast", np.maximum, "median")]
-# test2 = tmp[("pHluorin405", np.maximum, "median")]
-# ph = test / test2
-# ph = ph.stack().reset_index(1)
-# ph.columns = ["tp", "fl"]
-
-
-def m2p5_med(ext, ch, red=np.maximum):
-    m2p5pc = ext[(ch, red, "max2p5pc")]
-    med = ext[(ch, red, "median")]
-
-    result = m2p5pc / med
-
-    return result
-
-
-def plot_avg(df):
-    df = df.stack().reset_index(1)
-    df.columns = ["tp", "val"]
-
-    sns.relplot(x=df["tp"], y=df["val"], kind="line")
-    plt.show()
-
-
-def split_data(df: DataFrame, splits: List[int]):
-    dfs = [df.iloc[:, i:j] for i, j in zip((0,) + splits, splits + (df.shape[1],))]
-    return dfs
-
-
-def growth_rate(
-    data: Series, alg=None, filt={"kind": "savgol", "window": 7, "degree": 3}
-):
-    if alg is None:
-        alg = "standard"
-
-    if filt:  # TODO add support for multiple algorithms
-        window = filt["window"]
-        degree = filt["degree"]
-        data = Series(
-            non_uniform_savgol(
-                data.dropna().index, data.dropna().values, window, degree
-            ),
-            index=data.dropna().index,
-        )
-
-    diff_kernel = np.array([1, -1])
-
-    return Series(np.convolve(data, diff_kernel, "same"), index=data.dropna().index)
-
-
-# pp = PostProcessor(source=19831)
-# pp.load_tiler_cells()
-# f = "/home/alan/Documents/sync_docs/libs/postproc/gluStarv_2_0_x2_dual_phl_ura8_00/extraction"
-# pp.load_extraction(
-#     "/home/alan/Documents/sync_docs/libs/postproc/postprocessor/"
-#     + pp.expt.name
-#     + "/extraction/"
-# )
-# tmp = pp.extraction["phl_ura8_002"]
-
-
-def _check_bg(data):
-    for k in list(pp.extraction.values())[0].keys():
-        for p in pp.expt.positions:
-            if k not in pp.extraction[p]:
-                print(p, k)
-
-
-# data = {
-#     k: pd.concat([pp.extraction[pos][k] for pos in pp.expt.positions[:-3]])
-#     for k in list(pp.extraction.values())[0].keys()
-# }
-
-
-def hmap(df, **kwargs):
-    g = sns.heatmap(sort_df(df), robust=True, cmap="mako_r", **kwargs)
-    plt.xlabel("")
-    return g
-
-
-# from random import randint
-# x = randint(0, len(smooth))
-# plt.plot(clean.iloc[x], 'b')
-# plt.plot(smooth.iloc[x], 'r')
-# plt.show()
-
-
-# data = tmp
-# df = data[("general", None, "area")]
-# clean = clean_tracks(df, min_len=160)
-# clean = clean.loc[clean.notna().sum(axis=1) > 9]
-# gr = clean.apply(growth_rate, axis=1)
-# splits = (72, 108, 180)
-# gr_sp = split_data(gr, splits)
-
-# idx = gr.index
-
-# bg = get_bg(data)
-# test = data[("GFPFast", np.maximum, "median")]
-# test2 = data[("pHluorin405", np.maximum, "median")]
-# ph = (test / test2).loc[idx]
-# c = pd.concat((ph.mean(1), gr.max(1)), axis=1)
-# c.columns = ["ph", "gr_max"]
-# # ph = ph.stack().reset_index(1)
-# # ph.columns = ['tp', 'fl']
-
-# ph_sp = split_data(gr, splits)
-
-
-def get_bg(data):
-    bg = {}
-    fl_subkeys = [
-        x
-        for x in data.keys()
-        if x[0] in ["GFP", "GFPFast", "mCherry", "pHluorin405"]
-        and x[-1] != "imBackground"
-    ]
-    for k in fl_subkeys:
-        nk = list(k)
-        bk = tuple(nk[:-1] + ["imBackground"])
-        nk = tuple(nk[:-1] + [nk[-1] + "_BgSub"])
-        tmp = []
-        for i, v in data[bk].iterrows():
-            if i in data[k].index:
-                newdf = data[k].loc[i] / v
-                newdf.index = pd.MultiIndex.from_tuples([(*i, c) for c in newdf.index])
-            tmp.append(newdf)
-        bg[nk] = pd.concat(tmp)
-
-    return bg
-
-
-def calc_ph(bg):
-    fl_subkeys = [x for x in bg.keys() if x[0] in ["GFP", "GFPFast", "pHluorin405"]]
-    chs = list(set([x[0] for x in fl_subkeys]))
-    assert len(chs) == 2, "Too many channels"
-    ch1 = [x[1:] for x in fl_subkeys if x[0] == chs[0]]
-    ch2 = [x[1:] for x in fl_subkeys if x[0] == chs[1]]
-    inter = list(set(ch1).intersection(ch2))
-    ph = {}
-    for red_fld in inter:
-        ph[tuple(("ph",) + red_fld)] = (
-            bg[tuple((chs[0],) + red_fld)] / bg[tuple((chs[1],) + red_fld)]
-        )
-
-
-def get_traps(pp):
-    t0 = {}
-    for pos in pp.tiler.positions:
-        pp.tiler.current_position = pos
-        t0[pos] = pp.tiler.get_traps_timepoint(
-            0, channels=[0, pp.tiler.channels.index("mCherry")], z=[0, 1, 2, 3, 4]
-        )
-
-    return t0
-
-
-def get_pos_ph(pp):
-    pat = re.compile(r"ph_([0-9]_[0-9][0-9])")
-    return {
-        pos: float(pat.findall(pos)[0].replace("_", ".")) for pos in pp.tiler.positions
-    }
-
-
-def plot_sample_bf_mch(pp):
-    bf_mch = get_traps(pp)
-    ts = [{i: v[:, j, ...] for i, v in bf_mch.items()} for j in [0, 1]]
-    tsbf = {i: v[:, 0, ...] for i, v in bf_mch.items()}
-
-    posdict = {k: v for k, v in get_pos_ph(pp).items()}
-    posdict = {v: k for k, v in posdict.items()}
-    posdict = {v: k for k, v in posdict.items()}
-    ph = np.unique(list(posdict.values())).tolist()
-    counters = {ph: 0 for ph in ph}
-    n = [np.random.randint(ts[0][k].shape[0]) for k in posdict.keys()]
-
-    fig, axes = plt.subplots(2, 5)
-    for k, (t, name) in enumerate(zip(ts, ["Bright field", "mCherry"])):
-        for i, (pos, ph) in enumerate(posdict.items()):
-            # i = ph.index(posdict[pos])
-            axes[k, i].grid(False)
-            axes[k, i].set(
-                xticklabels=[],
-                yticklabels=[],
-            )
-            axes[k, i].set_xlabel(posdict[pos] if k else None, fontsize=28)
-            axes[k, i].imshow(
-                np.maximum.reduce(t[pos][n[i], 0], axis=2),
-                cmap="gist_gray" if not k else None,
-            )
-            # counters[posdict[pos]] += 1
-        plt.tick_params(
-            axis="x",  # changes apply to the x-axis
-            which="both",  # both major and minor ticks are affected
-            bottom=False,  # ticks along the bottom edge are off
-            top=False,  # ticks along the top edge are off
-            labelbottom=False,
-        )  # labels along the bottom edge are off
-        axes[k, 0].set_ylabel(name, fontsize=28)
-    plt.tight_layout()
-    plt.show()
-
-
-# Plotting calibration curve
-from scipy.optimize import curve_fit
-
-
-def fit_calibration(h):
-    ycols = [x for x in h.columns if "em_ratio" in x]
-    xcol = "ph"
-
-    def objective(x, a, b):
-        return a * x + b
-
-    # fig, axes = plt.subplots(1, len(ycols))
-    # for i, ycol in enumerate(ycols):
-    #     d = h[[xcol, ycol]]
-    #     params, _ = curve_fit(objective, *[d[col].values for col in d.columns])
-    #     sns.lineplot(x=xcol, y=ycol, data=h, alpha=0.5, err_style="bars", ax=axes[i])
-    #     # sns.lineplot(d[xcol], objective(d[xcol].values, *params), ax=axes[i])
-    # plt.show()
-
-    ycol = "em_ratio_mean"
-    d = h[[xcol, *ycols]]
-    tmp = d.groupby("ph").mean()
-    calibs = {ycol: curve_fit(objective, tmp.index, tmp[ycol])[0] for ycol in ycols}
-    # sns.lineplot(x=xcol, y=ycol, data=d, alpha=0.5, err_style="bars")
-    # plt.xlabel("pH")
-    # plt.ylabel("pHluorin emission ratio")
-    # sns.lineplot(d[xcol], objective(d[xcol], *params))
-
-    return calibs
diff --git a/postprocessor/old/postprocessor.py b/postprocessor/old/postprocessor.py
deleted file mode 100644
index 7e024d26..00000000
--- a/postprocessor/old/postprocessor.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import pkg_resources
-from pathlib import Path, PosixPath
-from typing import Union, List, Dict
-from datetime import datetime
-
-from compress_pickle import load, dump
-
-from core.experiment import Experiment
-from core.cells import Cells
-from core.segment import Tiler
-from core.io.matlab import matObject
-from core.experiment import Experiment
-
-
-class PostProcessor:
-    '''
-    Base class to perform feature extraction.
-    :param parameters: Parameters class with channels, reduction functions and
-        extraction functions to use.
-    :param source: Origin of experiment, if int it is assumed from Omero, if str
-        or Path
-    '''
-    def __init__(self, parameters=None, source: Union[int, str, Path] = None):
-        # self.params = parameters
-        if source is not None:
-            if type(source) is int:
-                self.expt_id = source
-                self.load_expt(source, omero=True)
-            elif type(source) is str or PosixPath:
-                self.load_expt(source, omero=False)
-
-    @property
-    def channels(self):
-        if not hasattr(self, '_channels'):
-            if type(self.params.tree) is dict:
-                self._channels = tuple(self.params.tree.keys())
-
-        return self._channels
-
-    @property
-    def current_position(self):
-        assert hasattr(self, 'expt'), 'No experiment loaded.'
-
-        if hasattr(self, 'tiler'):
-            assert self.expt.current_position.name == self.tiler.current_position
-
-        return self.expt.current_position
-
-    def load_expt(self, source: Union[int, str], omero: bool = False) -> None:
-        if omero:
-            self.expt = Experiment.from_source(
-                self.expt_id,  #Experiment ID on OMERO
-                'upload',  #OMERO Username
-                '***REMOVED***',  #OMERO Password
-                'islay.bio.ed.ac.uk',  #OMERO host
-                port=4064  #This is default
-            )
-        else:
-            self.expt = Experiment.from_source(source)
-            self.expt_id = self.expt.exptID
-
-    def load_tiler_cells(self) -> None:
-        self.tiler = Tiler(self.expt)
-        self.cells = Cells()
-        self.cells = self.cells.from_source(
-            self.expt.current_position.annotation)
-
-    def get_pos_mo_bud(self):
-        annot = self.expt._get_position_annotation(
-            self.expt.current_position.name)
-        matob = matObject(annot)
-        m = matob["timelapseTrapsOmero"].get("cellMothers", None)
-        if m is not None:
-            ids = np.nonzero(m.todense())
-            d = {(self.expt.current_position.name, i, int(m[i, j])): []
-                 for i, j in zip(*ids)}
-            for i, j, k in zip(*ids, d.keys()):
-                d[k].append((self.expt.current_position.name, i, j + 1))
-        else:
-            print("Pos {} has no mother matrix".format(
-                self.expt.current_position.name))
-            d = {}
-
-        return d
-
-    def get_exp_mo_bud(self):
-        d = {}
-        for pos in self.expt.positions:
-            self.expt.current_position = pos
-            d = {**d, **self.get_pos_mo_bud()}
-
-        self.expt.current_position = self.expt.positions[0]
-
-        return d
-
-    def load_extraction(self, folder=None) -> None:
-        if folder is None:
-            folder = Path(self.expt.name + '/extraction')
-
-        self.extraction = {}
-        for pos in self.expt.positions:
-            try:
-                self.extraction[pos] = load(folder / Path(pos + '.gz'))
-
-            except:
-                print(pos, ' not found')
diff --git a/postprocessor/processes/__init__.py b/postprocessor/processes/__init__.py
deleted file mode 100644
index e5a0d9b4..00000000
--- a/postprocessor/processes/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python3
diff --git a/postprocessor/processes/__pycache__/aggregate.cpython-37.pyc b/postprocessor/processes/__pycache__/aggregate.cpython-37.pyc
deleted file mode 100644
index a3329e1b0b398db7653dfa93286601c8f4c87a8b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2881
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

diff --git a/postprocessor/processes/__pycache__/base.cpython-37.pyc b/postprocessor/processes/__pycache__/base.cpython-37.pyc
deleted file mode 100644
index 338fe6150c0698a4364f21070ac77f7ddd552de5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 228
zcmZ?b<>g`kg0qto5)FX#V-N=hn1BoiATAaG5-AKRj5!Rsj8TlaOi@gX3@J>(44TX@
zfieu5Ot*Lg5{nXZQ%h2diXEMtZ*c__<tL{WgV=tW%(u7_)ANfG^^y{cQ;R@`7BK^f
zl?+9!AU25j<)L4kkyw<P5}%WqRIFc6nUYwXtzVE|T%ub5F)1@I9n6Pv^NaM8^NUjT
fq1@DBeURaL1(mlrY;yBcN^?@}7=h+~24V&P5)(a_

diff --git a/postprocessor/processes/__pycache__/bud_metric.cpython-37.pyc b/postprocessor/processes/__pycache__/bud_metric.cpython-37.pyc
deleted file mode 100644
index 6c98020d909ca54c89d360f709f984643ae6614d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2986
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
YTyaKsn|sea;%h?}$B0LK+uPpyA4)T{r2qf`

diff --git a/postprocessor/processes/__pycache__/davol.cpython-37.pyc b/postprocessor/processes/__pycache__/davol.cpython-37.pyc
deleted file mode 100644
index fd6c1cfe05e07980b127afa8c23a6d8a7dc373da..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 2827
zcmZuz&u<$=6rP#=;kDzWX$WmopctqqmS_S(h##sdXe$aFLZ$Sy0&TaRwX^Pe*PU6H
z#&S*xN-sHZ?;$yI<<FTbr~V7Q!1rctCk<I?-p=gIy!YmN-+Qxn78e@~&yV+I{K#kQ
z9~#Ua7oG3W^gRg4B+ppL?OTKn-Xe1cUg!<{(C6$vla6%vnRF9x&k1W7d(y|)Pkf96
z$+uX$_6nNVW?PuW2eFC=Ntr19{hhm}@jw-wL{q;*OJ)zbK#yLBNUZr#NDlM01J>o%
zWIcq*%Ge$c{R8H*um)p+xgg_bMRsCoyxM5tIr(6Rrj`|XFVSU3SX+$Io0@~BS0PHa
z$By_h<7~nwEE1AIKi_OS#?zx=qD&A)X`Yr*WZXd^6CBXhN!C>XHn+K{M^Pt>wT_~{
z*z2D*^meQg8D;5Xy)oR8v3{~KEOdEuSb>n{Jv*Kb3$@WHRI)K0CVImjYkjz*YEZvG
zPFkFJgO9JAIr93<u@1*Ndgx8d@1p4?hzUDFY^6x~F~!K)v|cL@Q{;MA6@y5ooe~45
zlW9c;E73&H)MV0)M_G9R18or$<I88gn67E_N^L-Z<%mB3YeW$s8)ejQM$ywzoK0tH
zQ6!5_6xm~%1&bgkvt39<#?0ar)lQOSST*+xR*2M!+*5Q^5SwWdf#ZrL*M2*nW<O)m
zX__Uj6fgOlYR%9~{C-V(FS+z(ZQl)pq%H$l-)Ep+LpJ2XJ_{Et1)IRKX*`R>_>pDW
zFOSPO&0FPm(gH6>gQQh-TQVN?wvh)dI)=9E(r)X<l#@4Fpbb{)R@~AcMwYY&1r(?j
zXxA!LJY2V15S+$vo>FfxnOICsFGEyJo*>j5DP6HxD8j~~+!m@%{mT$D7%i?AOQPFV
zv?|Peyi^f!Mltx{>$I20+5ha{rCpaGN>J@dpjJRSe##56SrJ26?wO@tQbtyJ^gE6?
z^Xdu|oP^oN*EnjFzen1u6bSgZUU`M@bIz0F)GQ2tqw|9G(I}<+C?`@J@+0S%t+8E>
zuP!;ZD<-1vm56p9wF@i6QT>?hI#rb`8xyL4-XCI(p@*tSy*+UcJlJ(7?$JWoJX)MM
z{UzzGk+lo-J{AOf=^rgyM)|0h0cq;9iCgW3U2AjV(VhvHwL@pupZEw=u=$Eo7a+Uw
z^Hk5zMrWe<ZK3IRA$rK@-&TJWyEsE>VV;O%@uzs|ZdK@^I*EK}e9#4T4@FF@e`a`U
zc>fHzuN$|r6m_u}3~#qdJ*BtL#vfG(&x5pnD=Rv2rf;J&TmR}`Ja0Hj1?4T$at*>d
zd8u-ef`Pg!x`6Pso2WO?+ji9Z*q~Yvh<4rsFHeYIdHErxK1G}Qhx2)9E|cfRrApGi
zs6M8>US5=WY#gK$oxL}Ru|TR~XuNnh%yx_zO5<-ODowPx*iDr#qi1oJN|bg|wxA1T
zzl<>Nj8j5zI81V>h<}PsW!w}Vn_7pmJJ-hV6nQ5u+coQPSg%qyN@W=2MKwUml(8yx
z=-X&$MOgwTRaIzbhoy6x`W*2OFtqv1#8uG{m&A2`O|<xm7*}`R`h4X)6=#Le=D<Vj
zKVS#EYuUdayx@J-x7X<*w;UF@P{lqg#p#)em@?ccoxVE}U_Pp#3=ZACcjO;aIjL{X
zV_uPE8uJrk+7x3@i~L7Q@Y4|&>T5Wru0tT_DeT6j8aFKfrdE}g=^ZqW{TEnR#h#Bp
zIm1m`K+Yqm`i87<<7LUSB%6Cp@DNlsov7GZrmj>>C6@MC_l;!Y>INyAvXqpDeihm?
z*`$c3U!hH1Rqvqx5b5VWNp`d%gz(n@j)5+gu>L4dpN<msIcC&N5?|69{?Ifm6)i2>
zZdI|rrFqhB_gd4lvT3I*SZkOkZQB0yi6&ohe163lubu~5dr{Gip)QhGAwk(|FHd_{
zSeH!wUR=gM(7&7_v`iCpF4NBRp0?p6tLkkM=U>spc>2q-UInfzR-9$Gxok6`T}R%I
z2Kd*ZBs2an&hfuwO^1!RSEzXXajX+XWK$Ho%0E(hVy)`g6bf!tKO48HmTLMv3wXen
Io#mzf0CYij^#A|>

diff --git a/postprocessor/processes/__pycache__/dsignal.cpython-37.pyc b/postprocessor/processes/__pycache__/dsignal.cpython-37.pyc
deleted file mode 100644
index ae3eaab217c9a2bd1df200c944881a311e458e84..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1462
zcmZWp&2G~`5Z?7VagsKW@*|)~V2(i|xpAl}C@q2mQdEhXFU!`uX&oHf>^drqa%-iY
zdg98VJ#ynkcmZEI^%XcVv$oS#9cjlqliA&GX1>XGx7#5ge|~%sza2vUqEU+rn=M#o
z6O<&9#w4Kdbz*1A0ycF6my(Y}GU=WW=_+>2f(F<P*#x_(T(FyxekOhI5^f-)zEgB}
zg%(qlD{bCw?-ZR~orcQbKE#k(7;Ls+nU|oHeKv3;4VZGJ^OM>q-JqeG<zvS*@T3P1
zwTd-qqWwg~SMSW<c6^YH!zhvI;f6n&P4|@c)3KjNQ<bGrk{dry{V+{TB;l8Fs{Qhk
zfj!x$MYB8{LW!*47uOCfgB4uR1>sO6z(7ZRsvF>gbX}w3u{C}X?Sb##r=d9zT1g&9
zduEs&NnySYv()6Tva%FWvTyCmO!Y8KwHj8YGQ%ptL3UKMIFFJj=X?cmSa5W{eyak5
zIxM6Rhk{>huf=}{HVbkN4VF$sFR)>3WI<nIA0YkqSf^7iqc8`9g|V@@l&S?Gg(uZm
z%;FrcDsedTC%17_Q6uORKu>@OoEII=r>UI9xL@V`+f2mOFCOPI4Z+QzN4kv<t%R{K
z1_*SJO5Fv|H6&PHjc_!k)`VqHwV3PNVb&LdxfBdSF|R@0=GZA2mpofVo=rU20NypF
zcj5-EQz%~0wtVxdEazL6D?1_%XR*kYpC2ecJP^sg@}tBT6{CEqGL=YZvY(>2<W)_x
zPPUwm8&tzK;4(c>IXNchbODW`OTsxt!^|?JZISwp#=P}IP}n@qZP7TtYM70Cj5k)K
z>D8>Nse+_0hu{X1C25|);RT%laVB&EqbZ#;=bT-Ti3@vnh9q)Fm&h}KgVRvB;(KIZ
zG}h7AC_J6UG1S4rtRkjLB!w%Z@wm<bF<xu2B^%ST<SF=T>?<{P@b^0!IjHZV!fNR?
zR0z8ykdw)x{a)na1O5+bOrz*l6$(`x*HuC?0oX@)&~oYl?C!%_a_Sz_=<9n$GZP8S
zv3?F_@B)W<IlKcL-obKs2YbROjWyTU&k`5jaxKRtjf>W1IT&xSjODvdkM+9$0e+lN
Ag#Z8m

diff --git a/postprocessor/processes/__pycache__/merger.cpython-37.pyc b/postprocessor/processes/__pycache__/merger.cpython-37.pyc
deleted file mode 100644
index 8c41739500db19de52db510b9f163a829183682f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1940
zcmZ`)OK&4Z5bmCrAF){@@dyV*!(Miz#Xf*IM2KBBC<qS8O3P_3qxN)<JxR~B=^iIa
zWKM~+Cw>5E54#c<ehL!&fw^)(k@y9isOqs3lNB}UazCo8>+x0j$#B>wK);k3zl;d^
z6Bio_VDcD-MJ9-#ij*{=rI&aV*PimrAPFe>nh0M6S40GoUiwK0>rh0nj-&_cNOYc&
zxO)v+NbEJkr`+&T*3#H7pX@jNr$%Se;yk9!U?%JISQizaDw*Mp4Rv7h7>4~6m?Q}m
zB=H1Id@+!L3}pmgAfgwc=!ouBm~>=U^hE!PT+*Z`cEEyRGb*K-Npouee?|%EGoZZ%
zTE%Lu_E@eoubDQisA^WvjkI&E1k*X=r_%75WDgcbiXy8ET`_5lHWOQmm{k%z;pW__
zM76bMnXYUhq+#`$X0_qji3O#SSqQ?IeUq=BD=f?5(5Zx%^1@hn9K|2rXe)AOQc*LI
z=2NTHvX*RF6}262dvR7&LZ9t9;Zv@b5^Zlc+0shEa&XS_)0tLjUMNV#_H7|&M#?+4
z7h0WHx-7V2?KxX;i>koG*EJnnhmfUfAJ|`i{EqbZqd07O5MBsimB}WurPlSliCVLo
zsMXpG%c4rbvQ$V_^9(TQ2!A~o`!MWBz*clcQnK=Z)0Ge0TLr-VRR}x~1oBM}<Iwa#
z)*!~s4zdBVA+R`V0xMPizXHD!a}VVH`F3LG9I~CNVrnOgbHVM&WT9>S@uCH8QO(?X
zy#y>|+Q`XzDea^M-FR`{bknqeWTojFpxL_0>qTDsUA%V}oa2pj1|W5Wzg-wE3h%&V
zMP5Q(3J>cKYwkt(ih}2z!-Lt+KOz10$YAIUR?qA<6KzCXZ0>}GHoe^FGDUPlJ^Gol
zE)FdlftscxWX_kW{ty-x14_KW^Jx2B?7rs4TKq5$4sMMx=ttbgkW^3zY1;JDwA5m$
za6U-W@0VPyPdaHTbOzg@tIQ^tYONX{s?)p)TAH0q0T!X9%(XCY!OoV&m_vhR8$=`y
zXL7d_goNU^J9H=i={1O3B>Mo^cCUF(au@BZZW3U`uSgO;Cjm(!h<emSt?w5{uAII*
z`s#>fbD5p6v-vrLs;#6HQ2d%rB{R#4ahA(7Ru`poyO}N*3L2wp52y;D<uqjoeI)!v
z<8WB?swS7@C0)^16kBo&danuXash1#gA;oO!PfL$e{)?I(%MT?7qc|I&8rjt1Qom2
z=*7L(n_C!etiadt&tV^hG2XQ+$76a74f#j!2XEzpCEg+82P!`H4N992kRj$^!`T$s
zVpegLbT<uuD{l^IJ_PAAxM1-<p`-AkzqSbu;*NP6b`92nc^BC(GIZM92i6SNPG(Z6
zjiU&Mm>Pyc4i)diYz)I<nRNZY8~6tI4&zSK=QC~ic*?Cb=(|D3llxfwH^n~2Vjt(r
l%5`o#ZX0~-Frv*V+w$uMk4^XU7VckQRlDa$bl{KTe*tLA<VXMj

diff --git a/postprocessor/processes/__pycache__/picker.cpython-37.pyc b/postprocessor/processes/__pycache__/picker.cpython-37.pyc
deleted file mode 100644
index ddf7c31ac2c8ba2e995849346fb59c9f6c85c7e2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 11532
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
aXV*bYi|=)<K41SxdCi)wY1UV*+J6H*(2(T-

diff --git a/postprocessor/processes/aggregate.py b/postprocessor/processes/aggregate.py
deleted file mode 100644
index cfe8e8f0..00000000
--- a/postprocessor/processes/aggregate.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from itertools import cycle
-
-import numpy as np
-import pandas as pd
-
-from agora.base import ParametersABC, ProcessABC
-
-
-class aggregateParameters(ParametersABC):
-    """
-    Parameters
-    reduction: str to be passed to a dataframe for collapsing across columns
-    """
-
-    def __init__(self, reductions):
-        super().__init__()
-        self.reductions = reductions
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({"reductions": ["mean", "median", "max"]})
-
-
-class aggregate(ProcessABC):
-    """
-    aggregate multiple datasets
-    """
-
-    def __init__(self, parameters: aggregateParameters):
-        super().__init__(parameters)
-
-    def run(self, signals):
-        names = np.array([signal.index.names for signal in signals])
-        index = signals[0].index
-        for s in signals[0:]:
-            index = index.intersection(s.index)
-
-        tmp_signals = [s.loc[index] for s in signals]
-        for i, s in enumerate(signals):
-            tmp_signals[i].name = s.name
-        signals = tmp_signals
-
-        assert len(signals), "Signals is empty"
-
-        bad_words = {
-            "postprocessing",
-            "extraction",
-            "None",
-            "np_max",
-            "",
-        }
-        get_keywords = lambda df: [
-            ind
-            for item in df.name.split("/")
-            for ind in item.split("/")
-            if ind not in bad_words
-        ]
-        colnames = [
-            "_".join(get_keywords(s) + [red])
-            for s in signals
-            for red in self.parameters.reductions
-        ]
-        concat = pd.concat(
-            [
-                getattr(signal, red)(axis=1)
-                for signal in signals
-                for red in self.parameters.reductions
-            ],
-            names=signals[0].index.names,
-            axis=1,
-        )
-        concat.columns = colnames
-
-        return concat
diff --git a/postprocessor/processes/base.py b/postprocessor/processes/base.py
deleted file mode 100644
index 142bd6c3..00000000
--- a/postprocessor/processes/base.py
+++ /dev/null
@@ -1 +0,0 @@
-from agora.base import ParametersABC, ProcessABC
\ No newline at end of file
diff --git a/postprocessor/processes/births.py b/postprocessor/processes/births.py
deleted file mode 100644
index e5a0d9b4..00000000
--- a/postprocessor/processes/births.py
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python3
diff --git a/postprocessor/processes/bud_metric.py b/postprocessor/processes/bud_metric.py
deleted file mode 100644
index 518ddf52..00000000
--- a/postprocessor/processes/bud_metric.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import numpy as np
-import pandas as pd
-from agora.base import ParametersABC, ProcessABC
-
-
-class bud_metricParameters(ParametersABC):
-    """
-    Parameters
-    """
-
-    def __init__(self, mode="longest"):
-        super().__init__()
-        self.mode = mode
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({"mode": "longest"})
-
-
-class bud_metric(ProcessABC):
-    """
-    Obtain the volume of daughter cells
-    if 'longest' assumes a single mother per trap.
-    """
-
-    def __init__(self, parameters: bud_metricParameters):
-        super().__init__(parameters)
-
-    def run(self, signal: pd.DataFrame):
-        if self.parameters.mode is "longest":
-            result = self.get_bud_metric_wrap(signal)
-
-        return result
-
-    @staticmethod
-    def get_bud_metric(signal):
-        mother_id = signal.index[signal.notna().sum(axis=1).argmax()]
-
-        nomother = signal.drop(mother_id)
-
-        starts = nomother.apply(pd.Series.first_valid_index, axis=1).sort_values()
-
-        ranges = [np.arange(i, j) for i, j in zip(starts[:-1], starts[1:])]
-        ranges.append(np.arange(starts.iloc[-1], signal.columns[-1]))
-
-        bud_metric = pd.concat(
-            [signal.loc[i, rng] for i, rng in zip(starts.index, ranges)]
-        )
-        srs = pd.Series(bud_metric, index=signal.columns, name=mother_id)
-
-        return srs
-
-    def get_bud_metric_wrap(self, signals):
-        srs = [
-            self.get_bud_metric(signals.loc[trap])
-            for trap in signals.index.unique(level="trap")
-        ]
-        index = [
-            (trap, mother.name)
-            for trap, mother in zip(signals.index.unique(level="trap"), srs)
-        ]
-
-        concatenated = pd.concat(srs, keys=index, axis=1, sort=True).T.sort_index()
-        concatenated.index.names = ["trap", "cell_label"]
-        return concatenated
diff --git a/postprocessor/processes/dsignal.py b/postprocessor/processes/dsignal.py
deleted file mode 100644
index 484ad599..00000000
--- a/postprocessor/processes/dsignal.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import pandas as pd
-
-from postprocessor.core.processes.base import ParametersABC, ProcessABC
-
-
-class dsignalParameters(ParametersABC):
-    """
-    :window: Number of timepoints to consider for signal.
-    """
-
-    def __init__(self, window: int):
-        self.window = window
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({"window": 3})
-
-
-class dsignal(ProcessABC):
-    """
-    Calculate the change in a signal depending on a window
-    """
-
-    def __init__(self, parameters: dsignalParameters):
-        super().__init__(parameters)
-
-    def run(self, signal: pd.DataFrame):
-        return signal.rolling(window=self.parameters.window, axis=1).mean().diff(axis=1)
diff --git a/postprocessor/processes/gpsignal.py b/postprocessor/processes/gpsignal.py
deleted file mode 100644
index f6cdb45f..00000000
--- a/postprocessor/processes/gpsignal.py
+++ /dev/null
@@ -1,97 +0,0 @@
-"""Gaussian process fit of a Signal."""
-import logging
-
-from postprocessor.core.processes.base import ParametersABC, ProcessABC
-import numpy as np
-import pandas as pd
-
-import gaussian_processes.gaussianprocess as gp
-
-def estimate_gr(volume, dt, noruns, bounds, verbose):
-    """
-    Parameters
-    ----------
-
-    volume : pd.Series
-        The volume series of a given cell
-    dt : float
-        The time interval in hours
-    noruns : int
-        The number of runs for optimisation
-    bounds : dict
-        The hyperparameter bounds used for optimisation
-    verbose : bool
-        If True, prints results
-
-    Returns
-    -------
-    """
-    volume = volume.values
-    n = len(volume)
-    idx = np.arange(n)
-    t = idx * dt
-    y = volume[volume > 0]
-    x = t[volume > 0]
-    idx = idx[volume > 0]
-    # Fit the GP
-    mg = gp.maternGP(bounds, x, y)
-    mg.findhyperparameters(noruns=noruns)
-    if verbose:
-        mg.results()  # Prints out the hyperparameters
-    mg.predict(x, derivs=2)  # Saves the predictions to object
-    # Save the predictions to a csv file so they can be reused
-    results = dict(time=mg.x, volume=mg.y, fit_time=mg.xnew, fit_volume=mg.f,
-                   growth_rate=mg.df, d_growth_rate=mg.ddf,
-                   volume_var=mg.fvar, growth_rate_var=mg.dfvar,
-                   d_growth_rate_var=mg.ddfvar)
-    for name, value in results.items():
-        results[name] = np.full((n, ), np.nan)
-        results[name][idx] = value
-    return results
-
-# Give that to a writer: NOTE the writer needs to learn how to write the
-# output of a process that returns multiple signals like this one does.
-
-class gpParameters(ParametersABC):
-    default_dict = dict(dt=5,
-             noruns=5,
-             bounds={0: (-2, 3),
-                     1: (-2, 1),
-                     2: (-4, -1)},
-             verbose=False)
-    def __init__(self, dt, noruns, bounds, verbose):
-        """
-        Parameters
-        ----------
-            dt : float
-                The time step between time points, in minutes
-            noruns : int
-                The number of times the optimisation is tried
-            bounds : dict
-                Hyperparameter bounds for the Matern Kernel
-            verbose : bool
-                Determines whether to print hyperparameter results
-        """
-        self.dt = dt
-        self.noruns = noruns
-        self.bounds = bounds
-        self.verbose = verbose
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict(cls.default_dict)
-
-
-class GPSignal(ProcessABC):
-    """Gaussian process fit of a Signal.
-    """
-    def __init__(self, parameters: gpParameters):
-        super().__init__(parameters)
-
-    def run(self, signal: pd.DataFrame):
-        results = signal.apply(lambda x: estimate_gr(x,
-                                                     **self.parameters.to_dict()),
-                               result_type='expand').T
-        multi_signal = {name: pd.DataFrame(np.vstack(results[name]))
-                        for name in results.columns}
-        return multi_signal
diff --git a/postprocessor/processes/merger.py b/postprocessor/processes/merger.py
deleted file mode 100644
index a79227fa..00000000
--- a/postprocessor/processes/merger.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from agora.base import ParametersABC, ProcessABC
-from postprocessor.core.functions.tracks import get_joinable
-
-
-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,
-        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
-    """
-
-    def __init__(
-        self,
-        tolerance: float,
-        smooth: bool = False,
-        window: int = 5,
-        degree: int = 3,
-        min_avg_delta: float = 0.9,
-    ):
-
-        self.tolerance = tolerance
-
-        self.smooth = smooth
-
-        self.window = window
-
-        self.degree = degree
-
-        self.min_avg_delta = min_avg_delta
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict(
-            {
-                "smooth": False,
-                "tolerance": 0.2,
-                "window": 5,
-                "degree": 3,
-                "min_avg_delta": 0.9,
-            }
-        )
-
-
-class merger(ProcessABC):
-    """
-    TODO check why it needs to be run a few times to complete the merging
-    """
-
-    def __init__(self, parameters):
-        super().__init__(parameters)
-
-    def run(self, signal):
-        joinable = get_joinable(signal, tol=self.parameters.tolerance)
-        # merged, _ = merge_tracks(signal)  # , min_len=self.window + 1)
-        # indices = (*zip(*merged.index.tolist()),)
-        # names = merged.index.names
-        # return {name: ids for name, ids in zip(names, indices)}
-        return joinable
diff --git a/postprocessor/processes/peaks.py b/postprocessor/processes/peaks.py
deleted file mode 100644
index 15ddf950..00000000
--- a/postprocessor/processes/peaks.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from scipy.signal import argrelmax, argrelmin
-from postprocessor.core.processes.base import ParametersABC, ProcessABC
-
-
-class PeaksParameters(ParametersABC):
-    """
-    Parameters
-        type : str {minima,  maxima, "all"}. Determines which type of peaks to identify
-        order : int Parameter to pass to scipy.signal.argrelextrema indicating
-            how many points to use for comparison.
-    """
-
-    def __init__(self, type):
-        self.type = type
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({"type": "minima", "order": 3})
-
-
-class Peaks(ProcessABC):
-    """
-    Identifies a signal sharply dropping.
-    """
-
-    def __init__(self, parameters: PeaksParameters):
-        super().__init__(parameters)
-
-    def run(self, signal: pd.DataFrame):
-        """
-        Returns a boolean dataframe with the same shape as the
-        original signal but with peaks as true values.
-        """
-        peaks_mat = np.zeros_like(signal, dtype=bool)
-
-        comparator = np.less if self.parameters.type is "minima" else np.greater
-        peaks_ids = argrelextrema(new_df, comparator=comparator, order=order)
-        peaks_mat[peak_ids] = True
-
-        return pd.DataFrame(
-            peaks_mat,
-            index=signal.index,
-            columns=signal.columns,
-        )
diff --git a/postprocessor/processes/picker.py b/postprocessor/processes/picker.py
deleted file mode 100644
index a7221e6f..00000000
--- a/postprocessor/processes/picker.py
+++ /dev/null
@@ -1,339 +0,0 @@
-import seaborn as sns
-from matplotlib import pyplot as plt  # TODO DELETE THIS
-
-from typing import Tuple, Union, List
-from abc import ABC, abstractmethod
-
-from itertools import groupby
-
-from utils_find_1st import find_1st, cmp_equal
-import numpy as np
-import pandas as pd
-
-from pcore.cells import CellsHDF
-
-from agora.base import ParametersABC, ProcessABC
-from postprocessor.core.functions.tracks import max_ntps, max_nonstop_ntps
-
-
-class pickerParameters(ParametersABC):
-    def __init__(
-        self,
-        sequence: List[str] = ["lineage", "condition"],
-    ):
-        self.sequence = sequence
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict(
-            {
-                "sequence": [
-                    # ("lineage", "intersection", "families"),
-                    ("condition", "intersection", "any_present", 0.8),
-                    ("condition", "intersection", "growing", 40),
-                    ("condition", "intersection", "present", 8),
-                    ("condition", "intersection", "mother_buds", 5, 0.8),
-                    # ("lineage", "full_families", "intersection"),
-                ],
-            }
-        )
-
-
-class picker(ProcessABC):
-    """
-    :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 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,
-        parameters: pickerParameters,
-        cells: CellsHDF,
-    ):
-        super().__init__(parameters=parameters)
-
-        self._cells = cells
-
-    @staticmethod
-    def mother_assign_to_mb_matrix(ma: List[np.array]):
-        # Convert from list of lists to mother_bud sparse matrix
-        ncells = sum([len(t) for t in ma])
-        mb_matrix = np.zeros((ncells, ncells), dtype=bool)
-        c = 0
-        for cells in ma:
-            for d, m in enumerate(cells):
-                if m:
-                    mb_matrix[c + d, c + m - 1] = True
-
-            c += len(cells)
-
-        return mb_matrix
-
-    @staticmethod
-    def mother_assign_from_dynamic(ma, label, trap, ntraps: int):
-        """
-        Interpolate the list of lists containing the associated mothers from the mother_assign_dynamic feature
-        """
-        idlist = list(zip(trap, label))
-        cell_gid = np.unique(idlist, axis=0)
-
-        last_lin_preds = [
-            find_1st(((label[::-1] == lbl) & (trap[::-1] == tr)), True, cmp_equal)
-            for tr, lbl in cell_gid
-        ]
-        mother_assign_sorted = ma[last_lin_preds]
-
-        traps = cell_gid[:, 0]
-        iterator = groupby(zip(traps, mother_assign_sorted), lambda x: x[0])
-        d = {key: [x[1] for x in group] for key, group in iterator}
-        nested_massign = [d.get(i, []) for i in range(ntraps)]
-
-        return nested_massign
-
-    def pick_by_lineage(self, signals, how):
-
-        idx = signals.index
-
-        if how:
-            mothers = set(self.mothers)
-            daughters = set(self.daughters)
-            # daughters, mothers = np.where(mother_bud_mat)
-
-            search = lambda a, b: np.where(
-                np.in1d(
-                    np.ravel_multi_index(np.array(a).T, np.array(a).max(0) + 1),
-                    np.ravel_multi_index(np.array(b).T, np.array(a).max(0) + 1),
-                )
-            )
-            if how == "mothers":
-                idx = mothers
-            elif how == "daughters":
-                idx = daughters
-            elif how == "daughters_w_mothers":
-                present_mothers = idx.intersection(mothers)
-                idx = set(
-                    [
-                        tuple(x)
-                        for m in present_mothers
-                        for x in np.array(self.daughters)[search(self.mothers, m)]
-                    ]
-                )
-
-                print("associated daughters: ", idx)
-            elif how == "mothers_w_daughters":
-                present_daughters = idx.intersection(daughters)
-                idx = set(
-                    [
-                        tuple(x)
-                        for d in present_daughters
-                        for x in np.array(self.mothers)[search(self.daughters, d)]
-                    ]
-                )
-            elif how == "full_families":
-                present_mothers = idx.intersection(mothers)
-                dwm_idx = set(
-                    [
-                        tuple(x)
-                        for m in present_mothers
-                        for x in np.array(self.daughters)[
-                            search(np.array(self.mothers), m)
-                        ]
-                    ]
-                )
-                present_daughters = idx.intersection(daughters)
-                mwd_idx = set(
-                    [
-                        tuple(x)
-                        for d in present_daughters
-                        for x in np.array(self.mothers)[
-                            search(np.array(self.daughters), d)
-                        ]
-                    ]
-                )
-                idx = mwd_idx.union(dwm_idx)
-            elif how == "families" or how == "orphans":
-                families = mothers.union(daughters)
-                if how == "families":
-                    idx = families
-                elif how == "orphans":
-                    idx = idx.diference(families)
-
-            idx = idx.intersection(signals.index)
-
-        return idx
-
-    def pick_by_condition(self, signals, condition, thresh):
-        idx = self.switch_case(signals, condition, thresh)
-        return idx
-
-    def get_mothers_daughters(self):
-        ma = self._cells["mother_assign_dynamic"]
-        trap = self._cells["trap"]
-        label = self._cells["cell_label"]
-        nested_massign = self.mother_assign_from_dynamic(
-            ma, label, trap, self._cells.ntraps
-        )
-        # mother_bud_mat = self.mother_assign_to_mb_matrix(nested_massign)
-
-        idx = set(
-            [
-                (tid, i + 1)
-                for tid, x in enumerate(nested_massign)
-                for i in range(len(x))
-            ]
-        )
-        mothers, daughters = zip(
-            *[
-                ((tid, m), (tid, d))
-                for tid, trapcells in enumerate(nested_massign)
-                for d, m in enumerate(trapcells, 1)
-                if m
-            ]
-        )
-        return mothers, daughters
-
-    def run(self, signals):
-        indices = set(signals.index)
-        self.mothers, self.daughters = self.get_mothers_daughters()
-        for alg, op, *params in self.sequence:
-            if alg is "lineage":
-                param1 = params[0]
-                new_indices = getattr(self, "pick_by_" + alg)(
-                    signals.loc[list(indices)], param1
-                )
-            else:
-                param1, *param2 = params
-                new_indices = getattr(self, "pick_by_" + alg)(
-                    signals.loc[list(indices)], param1, param2
-                )
-
-            if op is "union":
-                # new_indices = new_indices.intersection(set(signals.index))
-                new_indices = indices.union(new_indices)
-
-            indices = indices.intersection(new_indices)
-
-        return np.array(list(indices))
-
-    @staticmethod
-    def switch_case(
-        signals: pd.DataFrame,
-        condition: str,
-        threshold: Union[float, int, list],
-    ):
-        if len(threshold) == 1:
-            threshold = [_as_int(*threshold, signals.shape[1])]
-        case_mgr = {
-            "any_present": lambda s, thresh: any_present(s, thresh),
-            "present": lambda s, thresh: s.notna().sum(axis=1) > thresh,
-            "nonstoply_present": lambda s, thresh: s.apply(thresh, axis=1) > thresh,
-            "growing": lambda s, thresh: s.diff(axis=1).sum(axis=1) > thresh,
-            "mother_buds": lambda s, p1, p2: mother_buds_wrap(s, p1, p2)
-            # "quantile": [np.quantile(signals.values[signals.notna()], threshold)],
-        }
-        return set(signals.index[case_mgr[condition](signals, *threshold)])
-
-
-def any_present(signals, threshold):
-    """
-    Returns a mask for cells, True if there is a cell in that trap that was present for more than :threshold: timepoints.
-    """
-    any_present = pd.Series(
-        np.sum(
-            [
-                np.isin([x[0] for x in signals.index], i) & v
-                for i, v in (signals.notna().sum(axis=1) > threshold)
-                .groupby("trap")
-                .any()
-                .items()
-            ],
-            axis=0,
-        ).astype(bool),
-        index=signals.index,
-    )
-    return any_present
-
-
-from copy import copy
-
-
-def mother_buds(df, min_budgrowth_t, min_mobud_ratio):
-    """
-    Parameters
-    ----------
-    signals : pd.DataFrame
-    min_budgrowth_t: Minimal number of timepoints we lock reassignment after assigning bud
-    min_initial_size: Minimal mother-bud ratio at the assignment
-    #TODO incorporate bud-assignment data?
-
-    # If more than one bud start in the same time point pick the smallest one
-    """
-
-    ntps = df.notna().sum(axis=1)
-    mother_id = df.index[ntps.argmax()]
-    nomother = df.drop(mother_id)
-    if not len(nomother):
-        return []
-    nomother = (  # Clean short-lived cells outside our mother cell's timepoints
-        nomother.loc[
-            nomother.apply(
-                lambda x: x.first_valid_index() >= df.loc[mother_id].first_valid_index()
-                and x.last_valid_index() <= df.loc[mother_id].last_valid_index(),
-                axis=1,
-            )
-        ]
-    )
-
-    start = nomother.apply(pd.Series.first_valid_index, axis=1)
-
-    # clean duplicates
-    duplicates = start.duplicated(False)
-    if duplicates.any():
-        dup_tps = np.unique(start[duplicates])
-        idx, tps = zip(
-            *[(nomother.loc[start == tp, tp].idxmin(), tp) for tp in dup_tps]
-        )
-        start = start[~duplicates]
-        start = pd.concat(
-            (start, pd.Series(tps, index=idx, dtype="int", name="cell_label"))
-        )
-        nomother = nomother.loc[start.index]
-        nomother.index = nomother.index.astype("int")
-
-    d_to_mother = nomother[start] - df.loc[mother_id, start] * min_mobud_ratio
-    size_filter = d_to_mother[
-        d_to_mother.apply(lambda x: x.dropna().iloc[0], axis=1) < 0
-    ]
-    cols_sorted = (
-        size_filter.sort_index(axis=1)
-        .apply(pd.Series.first_valid_index, axis=1)
-        .sort_values()
-    )
-    if not len(cols_sorted):
-        return []
-    bud_candidates = cols_sorted.loc[
-        [True, *(np.diff(cols_sorted.values) > min_budgrowth_t)]
-    ]
-
-    return [mother_id] + [int(i) for i in bud_candidates.index.tolist()]
-
-
-def mother_buds_wrap(signals, *args):
-    ids = []
-    for trap in signals.index.unique(level="trap"):
-        df = signals.loc[trap]
-        selected_ids = mother_buds(df, *args)
-        ids += [(trap, i) for i in selected_ids]
-
-    idx_srs = pd.Series(False, signals.index).astype(bool)
-    idx_srs.loc[ids] = True
-    return idx_srs
-
-
-def _as_int(threshold: Union[float, int], ntps: int):
-    if type(threshold) is float:
-        threshold = ntps * threshold
-    return threshold
diff --git a/postprocessor/processes/savgol.py b/postprocessor/processes/savgol.py
deleted file mode 100644
index d0256736..00000000
--- a/postprocessor/processes/savgol.py
+++ /dev/null
@@ -1,152 +0,0 @@
-import numpy as np
-import pandas as pd
-
-from postprocessor.core.processes.base import ParametersABC, ProcessABC
-
-
-class savgolParameters(ParametersABC):
-    """
-    Parameters
-
-        window : int (odd)
-            Window length of datapoints. Must be odd and smaller than x
-        polynom : int
-            The order of polynom used. Must be smaller than the window size
-    """
-
-    def __init__(self, window, polynom):
-        self.window = window
-        self.polynom = polynom
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({"window": 3, "polynom": 2})
-
-
-class savgol(ProcessABC):
-    """
-    Apply Savitzky-Golay filter (works with NaNs, but it might return
-    NaN regions).
-    """
-
-    def __init__(self, parameters: savgolParameters):
-        super().__init__(parameters)
-
-    def run(self, signal: pd.DataFrame):
-        savgol_on_srs = lambda x: self.non_uniform_savgol(
-            x.index, x.values, self.parameters.window, self.parameters.polynom
-        )
-        return signal.apply(savgol_on_srs, 1)
-
-    @staticmethod
-    def non_uniform_savgol(x, y, window: int, polynom: int):
-        """
-        Applies a Savitzky-Golay filter to y with non-uniform spacing
-        as defined in x
-
-        This is based on https://dsp.stackexchange.com/questions/1676/savitzky-golay-smoothing-filter-for-not-equally-spaced-data
-        The borders are interpolated like scipy.signal.savgol_filter would do
-
-        source: https://dsp.stackexchange.com/a/64313
-
-        Parameters
-        ----------
-        x : array_like
-            List of floats representing the x values of the data
-        y : array_like
-            List of floats representing the y values. Must have same length
-            as x
-        window : int (odd)
-            Window length of datapoints. Must be odd and smaller than x
-        polynom : int
-            The order of polynom used. Must be smaller than the window size
-
-        Returns
-        -------
-        np.array of float
-            The smoothed y values
-        """
-        if len(x) != len(y):
-            raise ValueError('"x" and "y" must be of the same size')
-
-        if len(x) < window:
-            raise ValueError("The data size must be larger than the window size")
-
-        if type(window) is not int:
-            raise TypeError('"window" must be an integer')
-
-        if window % 2 == 0:
-            raise ValueError('The "window" must be an odd integer')
-
-        if type(polynom) is not int:
-            raise TypeError('"polynom" must be an integer')
-
-        if polynom >= window:
-            raise ValueError('"polynom" must be less than "window"')
-
-        half_window = window // 2
-        polynom += 1
-
-        # Initialize variables
-        A = np.empty((window, polynom))  # Matrix
-        tA = np.empty((polynom, window))  # Transposed matrix
-        t = np.empty(window)  # Local x variables
-        y_smoothed = np.full(len(y), np.nan)
-
-        # Start smoothing
-        for i in range(half_window, len(x) - half_window, 1):
-            # Center a window of x values on x[i]
-            for j in range(0, window, 1):
-                t[j] = x[i + j - half_window] - x[i]
-
-            # Create the initial matrix A and its transposed form tA
-            for j in range(0, window, 1):
-                r = 1.0
-                for k in range(0, polynom, 1):
-                    A[j, k] = r
-                    tA[k, j] = r
-                    r *= t[j]
-
-            # Multiply the two matrices
-            tAA = np.matmul(tA, A)
-
-            # Invert the product of the matrices
-            tAA = np.linalg.inv(tAA)
-
-            # Calculate the pseudoinverse of the design matrix
-            coeffs = np.matmul(tAA, tA)
-
-            # Calculate c0 which is also the y value for y[i]
-            y_smoothed[i] = 0
-            for j in range(0, window, 1):
-                y_smoothed[i] += coeffs[0, j] * y[i + j - half_window]
-
-            # If at the end or beginning, store all coefficients for the polynom
-            if i == half_window:
-                first_coeffs = np.zeros(polynom)
-                for j in range(0, window, 1):
-                    for k in range(polynom):
-                        first_coeffs[k] += coeffs[k, j] * y[j]
-            elif i == len(x) - half_window - 1:
-                last_coeffs = np.zeros(polynom)
-                for j in range(0, window, 1):
-                    for k in range(polynom):
-                        last_coeffs[k] += coeffs[k, j] * y[len(y) - window + j]
-
-        # Interpolate the result at the left border
-        for i in range(0, half_window, 1):
-            y_smoothed[i] = 0
-            x_i = 1
-            for j in range(0, polynom, 1):
-                y_smoothed[i] += first_coeffs[j] * x_i
-                x_i *= x[i] - x[half_window]
-
-        # Interpolate the result at the right border
-        for i in range(len(x) - half_window, len(x), 1):
-            y_smoothed[i] = 0
-            x_i = 1
-            for j in range(0, polynom, 1):
-                y_smoothed[i] += last_coeffs[j] * x_i
-                x_i *= x[i] - x[-half_window - 1]
-
-        return y_smoothed
diff --git a/postprocessor/processes/template.py b/postprocessor/processes/template.py
deleted file mode 100644
index 6fad758f..00000000
--- a/postprocessor/processes/template.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import numpy as np
-import pandas as pd
-
-from agora.base import ParametersABC, ProcessABC
-
-
-class TemplateParameters(ParametersABC):
-    """
-    Parameters
-    """
-
-    def __init__(
-        self,
-    ):
-        super().__init__()
-
-    @classmethod
-    def default(cls):
-        return cls.from_dict({})
-
-
-class Template(ProcessABC):
-    """
-    Template for process class.
-    """
-
-    def __init__(self, parameters: TemplateParameters):
-        super().__init__(parameters)
-
-    def run(self):
-        pass
diff --git a/postprocessor/processor.py b/postprocessor/processor.py
deleted file mode 100644
index 81c54b6f..00000000
--- a/postprocessor/processor.py
+++ /dev/null
@@ -1,316 +0,0 @@
-import h5py
-from typing import List, Dict, Union
-from pydoc import locate
-
-import numpy as np
-import pandas as pd
-
-from tqdm import tqdm
-
-from agora.base import ParametersABC
-from pcore.io.writer import Writer
-from pcore.io.signal import Signal
-
-from pcore.cells import Cells
-from postprocessor.core.processes.merger import mergerParameters, merger
-from postprocessor.core.processes.picker import pickerParameters, picker
-
-
-class PostProcessorParameters(ParametersABC):
-    """
-    Anthology of parameters used for postprocessing
-    :merger:
-    :picker: parameters for picker
-    :processes: Dict processes:[objectives], 'processes' are defined in ./processes/
-        while objectives are relative or absolute paths to datasets. If relative paths the
-        post-processed addresses are used.
-
-    """
-
-    def __init__(
-        self,
-        targets={},
-        parameters={},
-        outpaths={},
-    ):
-        self.targets: Dict = targets
-        self.parameters: Dict = parameters
-        self.outpaths: Dict = outpaths
-
-    def __getitem__(self, item):
-        return getattr(self, item)
-
-    @classmethod
-    def default(cls, kind=None):
-        if kind == "defaults" or kind == None:
-            return cls(
-                targets={
-                    "prepost": {
-                        "merger": "/extraction/general/None/area",
-                        "picker": ["/extraction/general/None/area"],
-                    },
-                    "processes": (
-                        (
-                            "bud_metric",
-                            [
-                                "/extraction/general/None/volume",
-                                "/extraction/em_ratio/np_max/mean",
-                                "/extraction/em_ratio/np_max/median",
-                            ],
-                        ),
-                        (
-                            "dsignal",
-                            [
-                                "/extraction/general/None/volume",
-                                "/extraction/em_ratio/np_max/mean",
-                                "/extraction/em_ratio/np_max/median",
-                                "/extraction/em_ratio_bgsub/np_max/mean",
-                                "/extraction/em_ratio_bgsub/np_max/median",
-                                "/postprocessing/bud_metric/extraction_general_None_volume",
-                                "/postprocessing/bud_metric/extraction_em_ratio_np_max_mean",
-                                "/postprocessing/bud_metric/extraction_em_ratio_np_max_median",
-                            ],
-                        ),
-                        (
-                            "aggregate",
-                            [
-                                [
-                                    "/extraction/general/None/volume",
-                                    "/extraction/em_ratio/np_max/mean",
-                                    "/extraction/em_ratio/np_max/median",
-                                    "/extraction/em_ratio_bgsub/np_max/mean",
-                                    "/extraction/em_ratio_bgsub/np_max/median",
-                                    "/extraction/gsum/np_max/median",
-                                    "/extraction/gsum/np_max/mean",
-                                    "postprocessing/bud_metric/extraction_general_None_volume",
-                                    "postprocessing/bud_metric/extraction_em_ratio_np_max_mean",
-                                    "postprocessing/bud_metric/extraction_em_ratio_np_max_median",
-                                    "postprocessing/dsignal/extraction_general_None_volume",
-                                    "postprocessing/dsignal/postprocessing_bud_metric_extraction_general_None_volume",
-                                    "postprocessing/dsignal/postprocessing_bud_metric_extraction_em_ratio_np_max_median",
-                                    "postprocessing/dsignal/postprocessing_bud_metric_extraction_em_ratio_np_max_mean",
-                                ]
-                            ],
-                        ),
-                        # "savgol": ["/extraction/general/None/area"],
-                    ),
-                },
-                parameters={
-                    "prepost": {
-                        "merger": mergerParameters.default(),
-                        "picker": pickerParameters.default(),
-                    }
-                },
-                outpaths={"aggregate": "/postprocessing/experiment_wide/aggregated/"},
-            )
-
-    def to_dict(self):
-        return {k: _if_dict(v) for k, v in self.__dict__.items()}
-
-
-class PostProcessor:
-    def __init__(self, filename, parameters):
-        self.parameters = parameters
-        self._filename = filename
-        self._signal = Signal(filename)
-        self._writer = Writer(filename)
-
-        # self.outpaths = parameters["outpaths"]
-        self.merger = merger(parameters["parameters"]["prepost"]["merger"])
-
-        self.picker = picker(
-            parameters=parameters["parameters"]["prepost"]["picker"],
-            cells=Cells.from_source(filename),
-        )
-        self.classfun = {
-            process: self.get_process(process)
-            for process, _ in parameters["targets"]["processes"]
-        }
-        self.parameters_classfun = {
-            process: self.get_parameters(process)
-            for process, _ in parameters["targets"]["processes"]
-        }
-        self.targets = parameters["targets"]
-
-    @staticmethod
-    def get_process(process):
-        """
-        Dynamically import a process class from the 'processes' folder.
-        Assumes process filename and class name are the same
-        """
-        return locate("postprocessor.core.processes." + process + "." + process)
-
-    @staticmethod
-    def get_parameters(process):
-        """
-        Dynamically import a process class from the 'processes' folder.
-        Assumes process filename and class name are the same
-        """
-        return locate(
-            "postprocessor.core.processes." + process + "." + process + "Parameters"
-        )
-
-    def run_prepost(self):
-        """Important processes run before normal post-processing ones"""
-
-        merge_events = self.merger.run(self._signal[self.targets["prepost"]["merger"]])
-
-        with h5py.File(self._filename, "r") as f:
-            prev_idchanges = self._signal.get_merges()
-
-        changes_history = list(prev_idchanges) + [np.array(x) for x in merge_events]
-        self._writer.write("modifiers/merges", data=changes_history)
-
-        with h5py.File(self._filename, "a") as f:  # TODO Remove this once done tweaking
-            if "modifiers/picks" in f:
-                del f["modifiers/picks"]
-
-        indices = self.picker.run(self._signal[self.targets["prepost"]["picker"][0]])
-        from collections import Counter
-
-        mothers, daughters = np.array(self.picker.mothers), np.array(
-            self.picker.daughters
-        )
-        self.tmp = [y for y in Counter([tuple(x) for x in mothers]).items() if y[1] > 2]
-        self._writer.write(
-            "postprocessing/lineage",
-            data=pd.MultiIndex.from_arrays(
-                np.append(mothers, daughters[:, 1].reshape(-1, 1), axis=1).T,
-                names=["trap", "mother_label", "daughter_label"],
-            ),
-            overwrite="overwrite",
-        )
-
-        # apply merge to mother-daughter
-        moset = set([tuple(x) for x in mothers])
-        daset = set([tuple(x) for x in daughters])
-        picked_set = set([tuple(x) for x in indices])
-        with h5py.File(self._filename, "a") as f:
-            merge_events = f["modifiers/merges"][()]
-        merged_moda = set([tuple(x) for x in merge_events[:, 0, :]]).intersection(
-            set([*moset, *daset, *picked_set])
-        )
-        search = lambda a, b: np.where(
-            np.in1d(
-                np.ravel_multi_index(a.T, a.max(0) + 1),
-                np.ravel_multi_index(b.T, a.max(0) + 1),
-            )
-        )
-
-        for target, source in merge_events:
-            if (
-                tuple(source) in moset
-            ):  # update mother to lowest positive index among the two
-                mother_ids = search(mothers, source)
-                mothers[mother_ids] = (
-                    target[0],
-                    self.pick_mother(mothers[mother_ids][0][1], target[1]),
-                )
-            if tuple(source) in daset:
-                daughters[search(daughters, source)] = target
-            if tuple(source) in picked_set:
-                indices[search(indices, source)] = target
-
-        self._writer.write(
-            "postprocessing/lineage_merged",
-            data=pd.MultiIndex.from_arrays(
-                np.append(mothers, daughters[:, 1].reshape(-1, 1), axis=1).T,
-                names=["trap", "mother_label", "daughter_label"],
-            ),
-            overwrite="overwrite",
-        )
-
-        self._writer.write(
-            "modifiers/picks",
-            data=pd.MultiIndex.from_arrays(
-                indices.T,
-                names=["trap", "cell_label"],
-            ),
-            overwrite="overwrite",
-        )
-
-    @staticmethod
-    def pick_mother(a, b):
-        """Update the mother id following this priorities:
-
-        The mother has a lower id
-        """
-        x = max(a, b)
-        if min([a, b]):
-            x = [a, b][np.argmin([a, b])]
-        return x
-
-    def run(self):
-        self.run_prepost()
-
-        for process, datasets in tqdm(self.targets["processes"]):
-            if process in self.parameters["parameters"].get(
-                "processes", {}
-            ):  # If we assigned parameters
-                parameters = self.parameters_classfun[process](self.parameters[process])
-
-            else:
-                parameters = self.parameters_classfun[process].default()
-
-            loaded_process = self.classfun[process](parameters)
-            for dataset in datasets:
-                # print("Processing", process, "for", dataset)
-
-                if isinstance(dataset, list):  # multisignal process
-                    signal = [self._signal[d] for d in dataset]
-                elif isinstance(dataset, str):
-                    signal = self._signal[dataset]
-                else:
-                    raise ("Incorrect dataset")
-
-                result = loaded_process.run(signal)
-
-                if process in self.parameters.to_dict()["outpaths"]:
-                    outpath = self.parameters.to_dict()["outpaths"][process]
-                elif isinstance(dataset, list):
-                    # If no outpath defined, place the result in the minimum common
-                    # branch of all signals used
-                    prefix = "".join(
-                        prefix + c[0]
-                        for c in takewhile(
-                            lambda x: all(x[0] == y for y in x), zip(*dataset)
-                        )
-                    )
-                    outpath = (
-                        prefix
-                        + "_".join(  # TODO check that it always finishes in '/'
-                            [d[len(prefix) :].replace("/", "_") for d in dataset]
-                        )
-                    )
-                elif isinstance(dataset, str):
-                    outpath = dataset[1:].replace("/", "_")
-                else:
-                    raise ("Outpath not defined", type(dataset))
-
-                if process not in self.parameters.to_dict()["outpaths"]:
-                    outpath = "/postprocessing/" + process + "/" + outpath
-
-                if isinstance(result, dict):  # Multiple Signals as output
-                    for k, v in result:
-                        self.write_result(
-                            outpath + f"/{k}",
-                            v,
-                            metadata={},
-                        )
-                else:
-                    self.write_result(
-                        outpath,
-                        result,
-                        metadata={},
-                    )
-
-    def write_result(
-        self, path: str, result: Union[List, pd.DataFrame, np.ndarray], metadata: Dict
-    ):
-        self._writer.write(path, result, meta=metadata)
-
-
-def _if_dict(item):
-    if hasattr(item, "to_dict"):
-        item = item.to_dict()
-    return item
-- 
GitLab