Source code for MidlineIdentifier.utilis

from typing import Literal, get_args, Union, Annotated

import numpy as np
import pandas as pd
import os
import logging

from scipy import optimize, integrate, interpolate
from scipy.stats import bootstrap


[docs] def ParseArgs(args): """ Parse arguments from the commandline. Parameters ---------- args : :class:`list` List of arguments Returns ------- fad, fimg, args.diskClosing, args.diskOpening, outdir, sample """ if args.sample is None: logging.info('sample name is None. Setting to default.') sample = 'BudoidObject' else: sample = str(args.sample) if (args.dir is not None) and os.path.isdir(args.dir): logging.info('Directory is provided. Looking for dapi_segmentation_mask.tif and spatial_anndata_spotipy.h5ad in it.') fimg = os.path.join(args.dir, 'dapi_segmentation_mask.tif') fad = os.path.join(args.dir, 'spatial_anndata_spotipy.h5ad') indir = args.dir elif args.dir is None: if os.path.isfile(args.fimg): fimg = args.fimg else: raise FileNotFoundError('Invalid image path.') if os.path.isfile(args.fad): fad = args.fad else: raise FileNotFoundError('Invalid anndata path.') indir = os.path.split(fimg)[0] outdir = indir if args.outdir is None else args.outdir os.makedirs(outdir, exist_ok = True) return fad, fimg, args.diskClosing, args.diskOpening, outdir, sample
[docs] def ScaleMinMax(x): """ Scale the input vector into the range between zero and one. Parameters ---------- x : :class:`array_like` Returns ------- :class:`array_like` Scaled x """ if not isinstance(x, np.ndarray): x = np.array(x) return (x - min(x)) / (max(x) - min(x))
[docs] def EuclideanDist(pts, pt): """ Calculate the Euclidean distance between one point and other point(s). Parameters ---------- pts : :class:`array_like` pt : :class:`array_like` of size one Returns ------- dist : :class:`float` or :class:`numpy.ndarray` Euclidean distance """ if not isinstance(pts, np.ndarray): pts = np.array(pts) if pt.shape[0] != 1: raise ValueError("pt must be of size one.") if pts.shape[0] == 1: dist = np.linalg.norm(pts - pt) else: dist = np.linalg.norm(pts - pt, axis=1) return dist
# get average exp by condition
[docs] def grouped_obs(adata, groupby, method, layer=None, gene_symbols=None): """ Get average exp by condition. Parameters ---------- adata : :class:`anndata.AnnData` Annotated data matrix. groupby : :class:`str` The key of the observations grouping to consider. method : :class:`str` Method used to aggregate the expression. Must be one of ``['sum','mean']`` layer : :class:`str` (default: `None`) Key from `adata.layers` whose value will be used to. If None, adata.X will be used. gene_symbols : :class:`list` | :class:`None` (default: `None`) Genes to aggregate. If None, calculation will be done for all genes Returns ------- :class:`pandas.DataFrame` A gene by group dataframe """ if method not in ['sum', 'mean']: raise ValueError(f"Method must be one of {['sum', 'mean']}.") if layer is not None: try: getX = lambda x: x.layers[layer] except: raise ValueError(f"layer must be one of {adata.layers.keys()}") else: getX = lambda x: x.X if gene_symbols is not None: excl = set(gene_symbols) - set(adata.var_names) genes = set(gene_symbols) & set(adata.var_names) if len(excl) > 0: logging.warning('%s is not in the adata, continue without them', excl) adata = adata[:,list(genes)] grouped = adata.obs.groupby(groupby) out = pd.DataFrame( np.zeros((adata.n_vars, len(grouped)), dtype=np.float64), columns=list(grouped.groups.keys()), index=adata.var_names ) if method == 'sum': for group, idx in grouped.indices.items(): X = getX(adata[idx, :]) out[group] = np.ravel(X.sum(axis=0, dtype=np.float64)).tolist() else: # mean for group, idx in grouped.indices.items(): X = getX(adata[idx, :]) out[group] = np.ravel(X.mean(axis=0, dtype=np.float64)).tolist() return out