{ "cells": [ { "cell_type": "markdown", "id": "c000", "metadata": {}, "source": [ "# Feature-set enrichment\n", "\n", "A well-level profile is hundreds to thousands of features. Reading them one by one is hopeless, and a single column rarely carries a biological claim on its own.\n", "\n", "`tl.enrich` scores biologically meaningful *sets* of features instead: a channel (one organelle stain), a measurement family, or a custom prior network. The answer is which *kinds* of measurement moved, not a list of columns, so a result reads at the level a screener reasons at.\n", "\n", "It wraps decoupler {cite:p}`Badia_i_Mompel_2022`, so the same call scores sets derived from the feature names and sets that come from prior knowledge.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "c001", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:01:28.589574Z", "iopub.status.busy": "2026-10-21T01:01:28.589180Z", "iopub.status.idle": "2026-10-21T01:01:32.601749Z", "shell.execute_reply": "2026-10-21T01:01:32.600184Z" } }, "outputs": [], "source": [ "import warnings\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import mantispy as mt\n", "\n", "# Palette shared with the mantispy plotters:\n", "ACCENT = \"crimson\" # the strongly-moved / significant set to report\n", "NULL = \"grey\" # background / not significant" ] }, { "cell_type": "markdown", "id": "c002", "metadata": {}, "source": [ "## From wells to a scored panel\n", "\n", "The preparation is the one from [Normalize, select, aggregate](../profiles/normalize_and_select.ipynb): plate-wise robust normalization, then drop the features flagged as degenerate, which have no spread in some plate and would otherwise dominate every score.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "c003", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:01:32.606151Z", "iopub.status.busy": "2026-10-21T01:01:32.605790Z", "iopub.status.idle": "2026-10-21T01:01:33.024344Z", "shell.execute_reply": "2026-10-21T01:01:33.023159Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ ":2: UserWarning: 2 of 467 features have no spread in at least one group of 'Metadata_Plate' among the rows selected by reference='negcon'. Adding epsilon=1e-18 to their scale multiplies them by up to 1e18, so they dominate every distance downstream. They are flagged in var['degenerate_scale'], and pp.feature_select drops them by default; to drop them now, adata = adata[:, ~adata.var['degenerate_scale']].copy(). A variance or outlier cut does not catch a feature that varies across a plate but is constant among its control wells; the flag does.\n", " mt.pp.normalize(adata, method=\"mad_robustize\", by=\"Metadata_Plate\")\n" ] }, { "data": { "text/plain": [ "{'features kept': 465, 'treated wells': 302, 'mechanisms': 12}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adata = mt.ds.bbbc021()\n", "mt.pp.normalize(adata, method=\"mad_robustize\", by=\"Metadata_Plate\")\n", "adata = adata[:, ~adata.var[\"degenerate_scale\"]].copy()\n", "\n", "# Keep the treatments that carry a mechanism label; DMSO and unannotated wells are dropped.\n", "treated = adata[~adata.obs[\"Metadata_Control\"].to_numpy()].copy()\n", "treated = treated[treated.obs[\"Metadata_MOA\"].notna().to_numpy()].copy()\n", "\n", "{\n", " \"features kept\": int(adata.n_vars),\n", " \"treated wells\": int(treated.n_obs),\n", " \"mechanisms\": int(treated.obs[\"Metadata_MOA\"].nunique()),\n", "}" ] }, { "cell_type": "markdown", "id": "c004", "metadata": {}, "source": [ "## Scoring feature-set activity\n", "\n", "`by=\"channel\"` groups the features by the organelle stain they were measured in, so each set is one channel. `method=\"ulm\"` fits a linear model per set and writes the per-well scores to `obsm[\"score_ulm\"]`, a wells-by-channels frame. Averaging those scores within each mechanism says which channels each mechanism moved.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "c005", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:01:33.026979Z", "iopub.status.busy": "2026-10-21T01:01:33.026812Z", "iopub.status.idle": "2026-10-21T01:01:57.316909Z", "shell.execute_reply": "2026-10-21T01:01:57.316165Z" } }, "outputs": [ { "data": { "text/html": [ " \n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mt.tl.enrich(treated, by=\"channel\", method=\"ulm\")\n", "\n", "mt.pl.sets_heatmap(treated, groupby=\"Metadata_MOA\", score_key=\"score_ulm\");" ] }, { "cell_type": "markdown", "id": "c006", "metadata": {}, "source": [ "The signed map shows its strongest positive scores for microtubule destabilizers on the actin channel (CorrActin) and for microtubule stabilizers on the tubulin channel (CorrTub), and Aurora kinase inhibitors read strongly negative on CorrActin. Several mechanisms are cross-talky, and the signed mean score can be muted when a channel gains and loses features at once: DNA damage stays slightly negative across all three channels rather than lighting up a single DNA block, and DNA replication is negative mainly on the DNA channel (CorrDAPI). For a specific mechanism the permutation view below is the better read.\n", "\n", "Read the off-target channels honestly. Morphology channels are cross-talky: a microtubule drug rounds the cell, and rounding moves the actin channel as well, so the actin score rises without actin being the drug's target. A channel is an interpretable summary of where the profile moved, not a one-to-one label for the process that moved it." ] }, { "cell_type": "markdown", "id": "c007", "metadata": {}, "source": "## The method suite and a robust consensus\n\n`enrich` exposes decoupler's set scorers through `method=`: `ulm`, `mlm`, `ora`, `aucell`, `gsea`, `gsva`, `zscore` and `viper`. They differ in how they turn a set's features into one score, so a call that only one of them supports is a call to distrust. Scoring the same channel sets with two unrelated methods, a linear model and a z-score, shows where they agree.\n" }, { "cell_type": "code", "execution_count": 4, "id": "c008", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:01:57.320225Z", "iopub.status.busy": "2026-10-21T01:01:57.319884Z", "iopub.status.idle": "2026-10-21T01:01:57.501318Z", "shell.execute_reply": "2026-10-21T01:01:57.500358Z" } }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mt.tl.enrich(treated, by=\"channel\", method=\"ulm\")\n", "ulm = treated.obsm[\"score_ulm\"].copy()\n", "\n", "mt.tl.enrich(treated, by=\"channel\", method=\"zscore\")\n", "zscore = treated.obsm[\"score_zscore\"].copy()\n", "\n", "\n", "def mean_per_mechanism(scores):\n", " \"\"\"Average each score column within a mechanism of action.\"\"\"\n", " grouped = scores.copy()\n", " grouped[\"Metadata_MOA\"] = treated.obs[\"Metadata_MOA\"].astype(str).to_numpy()\n", " return grouped.groupby(\"Metadata_MOA\").mean()\n", "\n", "\n", "ulm_means = mean_per_mechanism(ulm)\n", "zscore_means = mean_per_mechanism(zscore)\n", "\n", "channels = ulm_means.columns.intersection(zscore_means.columns)\n", "x = ulm_means[channels].to_numpy().ravel()\n", "y = zscore_means[channels].to_numpy().ravel()\n", "strong = np.abs(x) >= np.quantile(np.abs(x), 0.8)\n", "r = np.corrcoef(x, y)[0, 1]\n", "\n", "fig, ax = plt.subplots(figsize=(5, 5))\n", "ax.axhline(0, color=\"0.85\", lw=1)\n", "ax.axvline(0, color=\"0.85\", lw=1)\n", "ax.scatter(x[~strong], y[~strong], s=18, color=NULL, label=\"rest\")\n", "ax.scatter(x[strong], y[strong], s=22, color=ACCENT, label=\"top quintile |ulm|\")\n", "ax.set(xlabel=\"ulm mean score\", ylabel=\"zscore mean score\", title=f\"Two scorers agree (r = {r:.2f})\")\n", "ax.legend(frameon=False, fontsize=8)\n", "fig.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "c009", "metadata": {}, "source": [ "The two scorers place the same channels at the extremes, and every strongly-moved set sits in the same quadrant under both, so the sign of the call does not depend on which model produced it. The mean scores fall almost on one line.\n", "\n", "That agreement is what `method=\"consensus\"` automates. It runs a panel of these scorers and consolidates their calls, so no single method's bias decides the result.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "c010", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:01:57.504237Z", "iopub.status.busy": "2026-10-21T01:01:57.504053Z", "iopub.status.idle": "2026-10-21T01:02:21.216526Z", "shell.execute_reply": "2026-10-21T01:02:21.215268Z" } }, "outputs": [ { "data": { "text/html": [ " \n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mt.tl.enrich(treated, by=\"channel\", method=\"consensus\")\n", "\n", "mt.pl.sets_heatmap(treated, groupby=\"Metadata_MOA\", score_key=\"score_consensus\");" ] }, { "cell_type": "markdown", "id": "c011", "metadata": {}, "source": [ "The consensus map is on a smaller scale than the single-method map and keeps its positive actin and tubulin blocks, while the negative DNA-channel entries shrink toward zero. Prefer it for the call you report; reach for a single method when you specifically need its p-value or its speed. When the methods already agree, the consensus says nothing new, which is the point: the reported call no longer rides on the one scorer you happened to pick." ] }, { "cell_type": "markdown", "id": "c012", "metadata": {}, "source": [ "## Calibrated significance with permutation nulls\n", "\n", "The parametric `padj` a method reports assumes its score has a known null distribution. When that assumption is shaky, `n_permutations` replaces it with an empirical two-sided p-value: the set membership is shuffled that many times, and each observed score is ranked against the scores the shuffles produce. The significance then lives in `obsm[\"padj_ulm\"]`.\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "c013", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:02:21.220288Z", "iopub.status.busy": "2026-10-21T01:02:21.220078Z", "iopub.status.idle": "2026-10-21T01:02:25.986645Z", "shell.execute_reply": "2026-10-21T01:02:25.985474Z" } }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "with warnings.catch_warnings():\n", " warnings.filterwarnings(\"ignore\", message=\"you are shuffling\")\n", " mt.tl.enrich(treated, by=\"channel\", method=\"ulm\", n_permutations=200)\n", "\n", "mechanism = \"DNA damage\"\n", "in_mechanism = treated.obs[\"Metadata_MOA\"].astype(str).to_numpy() == mechanism\n", "significant = (treated.obsm[\"padj_ulm\"][in_mechanism] < 0.05).mean(axis=0).sort_values()\n", "\n", "fig, ax = plt.subplots(figsize=(6, 0.4 * len(significant) + 1.5))\n", "colours = np.where(significant.to_numpy() >= 0.5, ACCENT, NULL)\n", "ax.barh(np.arange(len(significant)), significant.to_numpy(), color=colours)\n", "ax.set_yticks(np.arange(len(significant)), significant.index, fontsize=7)\n", "ax.axvline(0.5, color=\"grey\", ls=\"--\", lw=1)\n", "ax.set(xlabel=f\"share of '{mechanism}' wells with permutation q < 0.05\", xlim=(0, 1))\n", "fig.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "c014", "metadata": {}, "source": [ "For DNA damage the DNA channel (CorrDAPI) is significant in the most wells, several times more often than the cross-talky cytoskeletal channels (CorrActin and CorrTub). No channel is significant in a majority of wells, but the ranking is the expected biology: DNA damage hits the nucleus. The permutation null recovers that organelle ranking even where the single-well signed score does not, and it reads significance off the shuffled sets themselves, which is the safer test when the parametric assumptions are unclear." ] }, { "cell_type": "markdown", "id": "c015", "metadata": {}, "source": [ "## Custom prior sets\n", "\n", "`enrich` scores any network with `source`, `target` and `weight` columns, where `source` names a set and `target` a feature in it. `feature_sets` returns exactly that shape, so a set can be built once and passed straight to `enrich(net=...)`.\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "c016", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T01:02:25.989576Z", "iopub.status.busy": "2026-10-21T01:02:25.989422Z", "iopub.status.idle": "2026-10-21T01:02:26.001426Z", "shell.execute_reply": "2026-10-21T01:02:26.000420Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
sourcetargetweight
0CorrActinCells_Intensity_IntegratedIntensityEdge_CorrActin1.0
1CorrTubCells_Intensity_IntegratedIntensityEdge_CorrTub1.0
2CorrActinCells_Intensity_IntegratedIntensity_CorrActin1.0
3CorrTubCells_Intensity_IntegratedIntensity_CorrTub1.0
4CorrActinCells_Intensity_LowerQuartileIntensity_CorrActin1.0
\n", "
" ], "text/plain": [ " source target weight\n", "0 CorrActin Cells_Intensity_IntegratedIntensityEdge_CorrActin 1.0\n", "1 CorrTub Cells_Intensity_IntegratedIntensityEdge_CorrTub 1.0\n", "2 CorrActin Cells_Intensity_IntegratedIntensity_CorrActin 1.0\n", "3 CorrTub Cells_Intensity_IntegratedIntensity_CorrTub 1.0\n", "4 CorrActin Cells_Intensity_LowerQuartileIntensity_CorrActin 1.0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net = mt.tl.feature_sets(treated, by=\"channel\")\n", "net.head()" ] }, { "cell_type": "markdown", "id": "c017", "metadata": {}, "source": [ "Three columns, `source`, `target` and `weight`, are the whole contract. Any curated set in this shape, a pathway, a measurement family, or a literature signature, scores through the same `enrich` call by way of `net=`. Building a biologically curated network is beyond this page; the point here is that the interface does not care where the sets came from.\n" ] }, { "cell_type": "markdown", "id": "c018", "metadata": {}, "source": [ "## Summary\n", "\n", "- `enrich` turns a wide profile into a handful of interpretable activity scores, one per feature set, at the level of organelles and measurement families.\n", "- Channels are an interpretable summary, not a clean process label: morphology channels cross-talk, so read the off-target scores with that in mind.\n", "- `method=\"consensus\"` runs a panel and consolidates it, so the reported call does not depend on a single scorer's bias.\n", "- `n_permutations` swaps the parametric significance for an empirical one that does not assume the score's null, the safer test when the assumptions are unclear.\n", "- Any `source`/`target`/`weight` network drops into `net=`, so a curated prior scores through the same call as the feature-derived sets.\n", "\n", "See also [Which measurements moved](../phenotypes/which_features_moved.ipynb), which reads the same feature annotation at the level of individual measurement families.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.15" } }, "nbformat": 4, "nbformat_minor": 5 }