{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial 2 — Filtering meta-programs\n", "\n", "Not all meta-programs that come out of the initial NMF are biologically\n", "meaningful or statistically robust. This tutorial demonstrates two\n", "complementary filtering strategies:\n", "\n", "1. **Quality-metric filter** (`filter_by_scores`) — remove MPs whose\n", " within-cluster cohesion metrics fall below thresholds.\n", "2. **Differential-activity filter** (`filter_for_significant_obs`) — keep\n", " only MPs whose cell scores differ significantly between an experimental\n", " condition and the rest, optionally with effect-size gates (KS statistic,\n", " Wasserstein distance).\n", "\n", "Both functions are **non-destructive**: they return a filtered copy of\n", "`results_mp` and a list of MPs to drop, leaving the original object\n", "unchanged so you can compare before and after.\n", "\n", "**Dataset** — Peter Sims lab DMSO vs. panobinostat scRNA-seq cohort." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0 · Imports and data preparation\n", "\n", "Run the same NMF + consensus steps as Tutorial 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import kaleidocell\n", "import scanpy as sc\n", "\n", "adata = sc.read_h5ad('../data/petersims_example.h5ad')\n", "\n", "results_nmf, nmf_convergence = kaleidocell.multi_sample_nmf(\n", " adata,\n", " batch_key='Patients',\n", " test_ranks=[4, 5, 6, 7, 8, 9],\n", " n_initializations=10,\n", " seed=42,\n", ")\n", "\n", "results_mp = kaleidocell.derive_nmf_metaprograms(results_nmf)\n", "mp_scores = kaleidocell.compute_mp_scores(results_mp, adata)\n", "\n", "print(f\"Starting with {len(results_mp['mp_dict'])} meta-programs.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1 · Filter by quality metrics\n", "\n", "`filter_by_scores` removes MPs that fail to meet minimum thresholds on\n", "the quality metrics computed during `derive_nmf_metaprograms`.\n", "\n", "**`logic` parameter**\n", "- `\"OR\"` (default) — drop an MP if it fails **any** threshold.\n", "- `\"AND\"` — drop an MP only if it fails **all** thresholds simultaneously\n", " (more lenient)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Inspect current metric distributions before deciding on thresholds\n", "results_mp['metrics']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "to_drop_quality, results_quality = kaleidocell.filter_by_scores(\n", " results_mp,\n", " meanSimilarity=0.2, # within-cluster cosine similarity\n", " silhouette=0.1, # cluster separation\n", " sampleCoverage=0.3, # fraction of samples represented\n", " logic='OR',\n", ")\n", "\n", "print(f\"MPs flagged by quality filter: {to_drop_quality}\")\n", "print(f\"Remaining: {len(results_quality['mp_dict'])} MPs\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2 · Filter by differential activity between conditions\n", "\n", "`filter_for_significant_obs` compares the per-cell MP scores between one\n", "condition (`value`) and all other cells. Multiple-testing correction is\n", "applied across MPs.\n", "\n", "**Available statistical tests**\n", "| `test` | Description |\n", "|--------|-------------|\n", "| `\"mannwhitney\"` | Mann–Whitney U (default; non-parametric location shift) |\n", "| `\"ttest\"` | Welch's t-test (parametric; fast for large n) |\n", "| `\"ks\"` | Kolmogorov–Smirnov (sensitive to any distributional difference) |\n", "| `\"permutation\"` | Permutation test (most rigorous; slow) |\n", "\n", "**Effect-size gates** (applied after significance)\n", "| Parameter | Description |\n", "|-----------|-------------|\n", "| `min_ks_stat` | Minimum KS statistic (≥ 0.20 recommended) |\n", "| `emd_null_n_bootstrap` | Bootstrap null EMD from the rest group; keeps only MPs above the null |\n", "\n", "The function always computes `ks_stat` and `emd` columns in the returned\n", "stats table, even when the gates are not applied." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Compare panobinostat-treated cells vs. DMSO controls\n", "to_drop_sig, results_sig, stats = kaleidocell.filter_for_significant_obs(\n", " results_mp,\n", " mp_scores,\n", " adata,\n", " obs='Treatment',\n", " value='Panobinostat',\n", " test='mannwhitney',\n", " correction='fdr_bh',\n", " p_threshold=0.05,\n", " min_ks_stat=0.20, # effect-size gate: drop small-effect MPs\n", " show_only_up_regulated=True, # keep only MPs up in Panobinostat\n", ")\n", "\n", "print(f\"\\nMPs retained: {list(results_sig['mp_dict'].keys())}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The stats DataFrame contains one row per MP with all test results\n", "stats[['statistic', 'pvalue_corrected', 'significant', 'ks_stat', 'emd',\n", " 'mean_condition', 'mean_rest', 'up_regulated']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3 · Apply the final drop list manually\n", "\n", "Both filter functions print the exact `drop_meta_programs` call needed.\n", "Run it only after reviewing the lists above." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Combine the two drop lists (deduplication is handled inside drop_meta_programs)\n", "final_drop = list(dict.fromkeys(to_drop_quality + to_drop_sig))\n", "print(f\"Final drop list ({len(final_drop)} MPs): {final_drop}\")\n", "\n", "results_mp_final = kaleidocell.drop_meta_programs(results_mp, final_drop)\n", "print(f\"Retained: {list(results_mp_final['mp_dict'].keys())}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4 · Generate HTML report for the filtered results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mp_scores_final = kaleidocell.compute_mp_scores(results_mp_final, adata)\n", "\n", "path = kaleidocell.get_html(\n", " results_mp_final,\n", " adata,\n", " mp_scores=mp_scores_final,\n", " obs=['Treatment', 'Patients'],\n", " output_path='../results/02/',\n", ")\n", "print(f\"Report: {path}\")" ] } ], "metadata": { "kernelspec": { "display_name": "kaleidocell_07", "language": "python", "name": "kaleidocell_07" }, "language_info": { "name": "python", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 5 }