Skip to content

Commit

Permalink
Further enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
mbernstein committed Aug 23, 2023
1 parent 6bef376 commit d376f9f
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 146 deletions.
8 changes: 6 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import monkeybread as mb
calc.neighborhood_profile
calc.cell_density
calc.ligand_receptor_score
calc.ligand_receptor_score_per_niche
calc.shortest_distances
calc.shortest_distances_pairwise
calc.cell_neighbors
Expand All @@ -36,16 +37,20 @@ import monkeybread as mb
.. autosummary::
:toctree: generated
plot.neighbors_profile_matrixplot
plot.cell_density
plot.location_and_density
plot.shortest_distances
plot.shortest_distances_pairwise
plot.cell_neighbor_embedding
plot.number_of_neighbors
plot.embedding_filter
plot.embedding_zoom
plot.ligand_receptor_scatter
plot.ligand_receptor_embedding
plot.ligand_receptor_embedding_zoom
plot.ligand_receptor_score_barplot_per_niche
plot.ligand_receptor_score_heatmap_per_niche
```

## Statistical Tests: `stat`
Expand All @@ -71,7 +76,6 @@ import monkeybread as mb
:toctree: generated
util.load_merscope
util.randomize_positions
util.subset_cells
util.contact_count
util.load_ligand_receptor_pairs_omnipath
```
9 changes: 7 additions & 2 deletions src/monkeybread/calc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
"""Module for calculations."""
from ._cell_neighbors import cell_neighbors, cell_neighbors_from_masks
from ._cell_neighbors import (
cell_neighbors,
cell_neighbors_from_masks,
cell_neighbors_per_niche,
cell_neighbors_per_niche_from_masks
)
from ._cell_density import cell_density
from ._neighborhood_profile import neighborhood_profile, cellular_niches
from ._shortest_distances import shortest_distances, shortest_distances_pairwise
from ._number_neighbors import number_of_neighbors, number_of_neighbors_from_masks
from ._ligand_receptor import ligand_receptor_score
from ._ligand_receptor import ligand_receptor_score, ligand_receptor_score_per_niche
125 changes: 122 additions & 3 deletions src/monkeybread/calc/_cell_neighbors.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def cell_neighbors(
Returns
-------
A mapping from cell ids in `group1` to sets of cell ids in `group2` indicating contact.
A mapping from cell ids in `group1` to sets of cell ids in `group2` that are its neighbors
(i.e., within `radius` distance).
"""
# Convert groups to lists if single group provided
if type(group1) == str:
Expand Down Expand Up @@ -83,7 +84,7 @@ def cell_neighbors_from_masks(
mask_group2: Iterable[bool],
basis="X_spatial",
radius=None,
):
) -> Dict[str, Set[str]]:
"""Calculate cell neighbors.
For each cell within a given group of cells, calculate all neighbors that are members of a
Expand Down Expand Up @@ -117,7 +118,8 @@ def cell_neighbors_from_masks(
Returns
-------
A mapping from cell ids in `group1` to sets of cell ids in `group2` indicating contact.
A mapping from cell ids in `group1` to sets of cell ids in `group2` that are its
neighbors (i.e., within `radius` distance).
"""
# Key for coordinates
obsm_key = basis
Expand Down Expand Up @@ -154,3 +156,120 @@ def cell_neighbors_from_masks(
cell_to_neighbors[cell] = neighbors

return cell_to_neighbors


def cell_neighbors_per_niche(
adata: AnnData,
niche_key: str,
groupby: str,
group1: Union[str, List[str]],
group2: Union[str, List[str]],
basis: Optional[str] = "X_spatial",
radius: Optional[float] = None,
) -> Dict[str, Dict[str, Set[str]]]:
"""
Calculate cell neighbors, but within the cellular niches calculated by
:func:`monkeybread.calc.cellular_niches`.
This function is a wrapper around :func:`monkebread.calc.cell_neighbors`
that calls this function separately on cells within each niche
Parameters
----------
adata
Annotated data matrix.
niche_key
The column in `adata.obs` storing the annotated niche of each cells
as calculated by :func:`monkeybread.calc.cellular_niches`
groupby
A categorical column in `adata.obs` to classify groups.
group1
Either one group or a list of groups from `adata.obs[groupby]`.
group2
Either one group or a list of groups from `adata.obs[groupby]`.
basis
Coordinates in `adata.obsm[{basis}]` to use. Defaults to `spatial`.
radius
The radius in which cells are considered touching. If not provided, will be calculated using
half of the average radius of group1 + half of the average radius of group2. This requires
width and height columns to be present in `adata.obs`.
Returns
-------
A mapping from each niche id to another mapping that maps each cell id in `group1` to sets
of cell ids in `group2` that are its neighbors (i.e., within `radius` distance).
"""
niche_to_cell_to_neighbors = {}
for niche in sorted(set(adata.obs[niche_key])):
adata_niche = adata[adata.obs[niche_key] == niche]

cell_to_neighbors = cell_neighbors(
adata_niche,
groupby,
group1,
group2,
basis,
radius,
)
niche_to_cell_to_neighbors[niche] = cell_to_neighbors
return niche_to_cell_to_neighbors


def cell_neighbors_per_niche_from_masks(
adata: AnnData,
niche_key: str,
mask_group1: Iterable[bool],
mask_group2: Iterable[bool],
basis="X_spatial",
radius=None,
) -> Dict[str, Dict[str, Set[str]]]:
"""
Calculate cell neighbors, but within the cellular niches calculated by
:func:`monkeybread.calc.cellular_niches_from_masks`. This is similar to
:func:`monkeybread.calc.cellular_niches_per_niche`, but differs from it based on how the two
groups are defined. This function accepts two masks (Boolean valued iterables). The first
mask defines the cells in the first group (those indices set to `True`) and the second mask
defines the cells in the second group.
This function is a wrapper around :func:`monkebread.calc.cell_neighbors_from_masks`
that calls this function separately on cells within each niche.
Parameters
----------
adata
Annotated data matrix.
mask_group1
An iterable of Boolean values specifying cells in the first group (all indices set to `True`).
mask_group2
An iterable of Boolean values specifying cells in the second group (all indices set to `True`).
basis
Coordinates in `adata.obsm[basis]` to use. Defaults to `X_spatial`.
radius
The radius in which cells are considered touching. If not provided, will be calculated using
half of the average radius of group1 + half of the average radius of group2. This requires
width and height columns to be present in `adata.obs`.
Returns
-------
A mapping from each niche id to another mapping that maps each cell id in `group1` to sets
of cell ids in `group2` that are its neighbors (i.e., within `radius` distance).
"""
niche_to_cell_to_neighbors = {}
for niche in sorted(set(adata.obs[niche_key])):
adata_niche = adata[adata.obs[niche_key] == niche]

mask_group1_niche = np.array(mask_group1)[adata.obs[niche_key] == niche]
mask_group2_niche = np.array(mask_group2)[adata.obs[niche_key] == niche]

cell_to_neighbors=cell_neighbors_from_masks(
adata_niche,
mask_group1_niche,
mask_group2_niche,
basis,
radius,
)
niche_to_cell_to_neighbors[niche] = cell_to_neighbors
return niche_to_cell_to_neighbors



62 changes: 54 additions & 8 deletions src/monkeybread/calc/_ligand_receptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,25 @@

def ligand_receptor_score(
adata: AnnData,
contacts: Dict[str, Set[str]],
cell_to_neighbors: Dict[str, Set[str]],
lr_pairs: Optional[Union[Tuple[str, str], List[Tuple[str, str]]]] = None,
) -> Dict[Tuple[str, str], float]:
"""
"Calculates an average co-expression score of a
Calculates an average co-expression score of a
ligand-receptor pair between neighboring cells..
Statistical test is as described in :cite:p:`He2021.11.03.467020` (See
Figure 4).
Calculates scores for ligand-receptor pairs in contacting cells.
Score calculation is as described in :cite:p:`He2021.11.03.467020`.
Calculates scores for ligand-receptor pairs in neighboring cells.
Parameters
----------
adata
Annotated data matrix.
contacts
The cell contacts, as calculated by :func:`monkeybread.calc.cell_contact`.
cell_to_neighbors
A mapping of cells to their neighbors, as calculated by
:func:`monkeybread.calc.cell_neighbors`.
lr_pairs
One or multiple tuples corresponding to (ligand, receptor). If `None` then
ligand/receptor pairs will be downloaded from Omnipath via the
Expand Down Expand Up @@ -65,7 +64,7 @@ def ligand_receptor_score(
list(
itertools.chain.from_iterable(
itertools.product([cell], neighbors)
for cell, neighbors in contacts.items()
for cell, neighbors in cell_to_neighbors.items()
)
)
)
Expand All @@ -82,3 +81,50 @@ def ligand_receptor_score(

return lr_pair_to_score


def ligand_receptor_score_per_niche(
adata: AnnData,
niche_to_cell_to_neighbors: Dict[str, Set[str]],
lr_pairs: Optional[Union[Tuple[str, str], List[Tuple[str, str]]]] = None,
) -> Dict[str, Dict[Tuple[str, str], float]]:
"""
Calculates an average co-expression score of a
ligand-receptor pair between neighboring cells within each cellular niche
calculated by :func:`monkeybread.calc.cellular_niches`.
Statistical test is as described in :cite:p:`He2021.11.03.467020` (See
Figure 4).
This function is a wrapper around :func:`monkeybread.calc.ligand_receptor_score`
and calls this function separately for each niche.
Parameters
----------
adata
Annotated data matrix.
cell_to_neighbors
A mapping of cells to their neighbors, as calculated by
:func:`monkeybread.calc.cell_neighbors`.
lr_pairs
One or multiple tuples corresponding to (ligand, receptor). If `None` then
ligand/receptor pairs will be downloaded from Omnipath via the
:func:`omnipath.interactions.import_intercell_network`.
Returns
-------
A dictionary mapping each niche to a sub-dictionary mapping each ligand-receptor
pair to its co-expression score.
"""
niche_to_lr_pair_to_score = {}
for niche, cell_to_neighbors in niche_to_cell_to_neighbors.items():

lr_pair_to_score = ligand_receptor_score(
adata,
cell_to_neighbors,
lr_pairs,
)
niche_to_lr_pair_to_score[niche] = lr_pair_to_score
return niche_to_lr_pair_to_score



9 changes: 7 additions & 2 deletions src/monkeybread/plot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
"""Module for plotting."""
from ._cell_transcript_proximity import cell_transcript_proximity
from ._embedding_other import embedding_filter, embedding_zoom
from ._cell_density import cell_density, location_and_density
from ._shortest_distances import shortest_distances, shortest_distances_pairwise
from ._number_neighbors import number_of_neighbors
from ._ligand_receptor import ligand_receptor_embedding, ligand_receptor_embedding_zoom, ligand_receptor_scatter
from ._ligand_receptor import (
ligand_receptor_embedding,
ligand_receptor_embedding_zoom,
ligand_receptor_scatter,
ligand_receptor_score_barplot_per_niche,
ligand_receptor_score_heatmap_per_niche
)
from ._neighborhood_profile import neighbors_profile_matrixplot
from ._cell_neighbors import cell_neighbor_embedding

Expand Down
Loading

0 comments on commit d376f9f

Please sign in to comment.