Source code for mantispy.pl._features

"""Plots about the feature space itself."""

from __future__ import annotations

from typing import TYPE_CHECKING

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from mantispy._core._corr import corr_matrix
from mantispy._core._reduce import get_matrix
from mantispy._core._utils import as_frame, feature_mask

if TYPE_CHECKING:
    from anndata import AnnData


[docs] def feature_correlation( adata: AnnData, key: str | None = "selected", groupby: str = "feature_group", max_features: int = 300, ax: plt.Axes | None = None, ): """Correlation heatmap with features ordered by their annotation. Features are sorted by ``groupby`` and then by channel, with a line at each group boundary. Ordering by annotation instead of clustering shows directly whether correlated features fall within the same measurement family. Args: adata: Object to draw. Usually well-level profiles. key: Restrict to features flagged by this boolean ``var`` column. ``None`` uses all. groupby: ``var`` column to order and delimit by. max_features: Draw at most this many features, taken in the sorted order. ax: Axes to draw into. """ mask = feature_mask(adata, key) annotation = as_frame(adata.var).loc[mask, [groupby, "channel"]].astype(str) order = annotation.sort_values([groupby, "channel"]).index[:max_features] positions = adata.var_names.get_indexer(order) correlation = corr_matrix(get_matrix(adata)[:, positions]) ax = ax or plt.subplots(figsize=(7, 6))[1] image = ax.imshow(np.nan_to_num(correlation, nan=0.0), cmap="RdBu_r", vmin=-1, vmax=1) labels = as_frame(adata.var).loc[order, groupby].astype(str).to_numpy() boundaries = np.flatnonzero(labels[1:] != labels[:-1]) + 0.5 for boundary in boundaries: ax.axhline(boundary, color="black", lw=0.5) ax.axvline(boundary, color="black", lw=0.5) centres = np.concatenate([[0], boundaries, [len(labels)]]) ticks = (centres[:-1] + centres[1:]) / 2 ax.set_xticks(ticks) ax.set_xticklabels(pd.unique(labels), rotation=90, fontsize=7) ax.set_yticks(ticks) ax.set_yticklabels(pd.unique(labels), fontsize=7) ax.set_title(f"feature correlation ({len(order)} features)", fontsize=9) ax.figure.colorbar(image, ax=ax, fraction=0.04, label="correlation") return ax
[docs] def feature_groups(adata: AnnData, key: str | None = None, ax: plt.Axes | None = None): """How many features each group contributes, split by channel. Pass ``key="selected"`` after feature selection to see which families survived. """ mask = feature_mask(adata, key) annotation = as_frame(adata.var).loc[mask, ["feature_group", "channel"]].astype(str) counts = annotation.value_counts().unstack(fill_value=0) ax = ax or plt.subplots(figsize=(7, 4))[1] counts.plot.bar(stacked=True, ax=ax) ax.set_ylabel("features") ax.set_xlabel("feature group") ax.legend(title="channel", fontsize=6, title_fontsize=7) ax.tick_params(axis="x", rotation=45) return ax