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