Source code for spatialtis.basic.basic

from ast import literal_eval
from itertools import combinations_with_replacement
from typing import Optional

import numpy as np
import pandas as pd
from anndata import AnnData
from shapely.geometry import MultiPoint

from spatialtis.abc import AnalysisBase
from spatialtis.typing import Number
from spatialtis.utils import doc


[docs]@doc class cell_components(AnalysisBase): """Count the proportion of each types of cells in each group Args: data: {adata} **kwargs: {analysis_kwargs} """ def __init__(self, data: AnnData, **kwargs): super().__init__(data, task_name="cell_components", **kwargs) counter = self.type_counter() self.result = pd.melt(counter, id_vars=self.exp_obs, var_name="type")
[docs]@doc class cell_density(AnalysisBase): """Calculating cell density in each ROI The size of each ROI will be auto-computed, it's the area of convex hull of all the cells in a ROI Args: data: {adata} ratio: The ratio between the unit used in your dataset and real length unit, default is 1.0; ratio = Dataset unit / real length unit; For example, if the resolution of your dataset is 1μm, but you want to use 1mm as unit, then you should set the ratio as 0.001, 1 pixels represent 0.001mm length. **kwargs: {analysis_kwargs} """ def __init__(self, data: AnnData, ratio: Number = 1.0, **kwargs): super().__init__(data, task_name="cell_density", **kwargs) counter = self.type_counter() df = counter.iloc[:, len(self.exp_obs)::] groups = data.obs[self.exp_obs + [self.centroid_key]].groupby(self.exp_obs) need_eval = self.is_col_str(self.centroid_key) area = [] for _, g in groups: if need_eval: cells = [literal_eval(c) for c in g[self.centroid_key].tolist()] else: cells = [c for c in g[self.centroid_key].tolist()] area.append(MultiPoint(cells).convex_hull.area) area = np.asarray(area) * ratio * ratio results = df.div(area, axis=0) results = pd.concat([counter.loc[:, self.exp_obs], results], axis=1) self.result = pd.melt(results, id_vars=self.exp_obs, var_name="type")
[docs]@doc class cell_morphology(AnalysisBase): """Cell morphology variation between different groups Args: data: {adata} metric_key: Which key in `AnnData.obs` to measure morphology (Default: eccentricity key) **kwargs: {analysis_kwargs} """ def __init__(self, data: AnnData, metric_key: Optional[str] = None, **kwargs): super().__init__(data, task_name="cell_morphology", **kwargs) if metric_key is None: metric_key = self.eccentricity_key result = data.obs.loc[:, self.exp_obs + [self.cell_type_key, metric_key]] result.rename( columns={self.cell_type_key: "type", metric_key: "value"}, inplace=True ) self.result = result
[docs]@doc class cell_co_occurrence(AnalysisBase): """The likelihood of two type of cells occur simultaneously in a ROI Args: data: {adata} **kwargs: {analysis_kwargs} """ def __init__(self, data: AnnData, **kwargs): super().__init__(data, task_name="cell_co_occurrence", **kwargs) counter = self.type_counter() df = counter.iloc[:, len(self.exp_obs)::] # normalize it using mean, greater than mean suggest it's occurrence df = ((df - df.mean()) / (df.max() - df.min()) > 0).astype(int) # generate combination of cell types cell_comb = [i for i in combinations_with_replacement(df.columns, 2)] chunks = [] for c in cell_comb: # if two type of cells are all 1, the result is 1, if one is 0, the result is 0 co = pd.DataFrame({"co_occur": df[c[0]] * df[c[1]]}) co["type1"] = c[0] co["type2"] = c[1] co = pd.concat([co, counter.loc[:, self.exp_obs]], axis=1) chunks.append(co) self.result = pd.concat(chunks)[ self.exp_obs + ["type1", "type2", "co_occur"] ].reset_index(drop=True)