--- name: sc-cell-communication description: Load when computing cell-cell ligand-receptor communication on an annotated scRNA AnnData via builtin scorer, LIANA, CellPhoneDB, CellChat (R), or NicheNet (R). Skip when assigning cell-type labels (use sc-cell-annotation); transcription factor → target regulatory networks (use sc-grn). trigger: cell communication, cell-cell communication, ligand receptor, cellchat, liana, cellphonedb, nichenet tags: - singlecell - scrna - cell-communication - ligand-receptor - liana - cellphonedb - cellchat - nichenet --- # sc-cell-communication ## When to use The user has an annotated scRNA AnnData (cell-type labels in `obs["cell_type"]` or another column passed via `--cell-type-key`) and wants ligand-receptor / sender-receiver interaction tables and figures. Five backends: - `builtin` (default) — compact curated L-R set, heuristic score, no p-values. - `liana` — Python LIANA rank aggregation (recommended general default). - `cellphonedb` — official CellPhoneDB statistical workflow (human-only). - `cellchat_r` — R-backed CellChat with pathway / centrality outputs. - `nichenet_r` — R-backed NicheNet ligand prioritisation; needs explicit `--receiver` + `--senders` + `--condition-*` (human-only). For TF → target gene regulatory networks use `sc-grn`. For cell-type labelling use `sc-cell-annotation`. ## Use in an analysis step Use `load_skill` from the notebook SDK; write returned objects with `write_output`. This runnable example is also in `examples/example_step.py`. The CLI remains available for standalone reports and galleries. ```python # Rank curated ligand-receptor mean products across PBMC clusters. # Reads pbmc3k_processed and uses its log-normalized raw snapshot. # Calls sc-cell-communication: communicate, sender_receiver_summary, interaction_heatmap_figure. # The builtin method does not test statistical significance. from skills._sdk.notebook import load_demo, load_skill, write_output communication = load_skill('sc-cell-communication') adata = load_demo('pbmc3k_processed').raw.to_adata() table = communication.communicate(adata, cell_type_key='louvain') write_output(table, 'tables/lr_interactions.csv') write_output(communication.sender_receiver_summary(table), 'tables/sender_receiver.csv') write_output(communication.interaction_heatmap_figure(table), 'figures/interaction_heatmap.png') assert not table.empty assert table.pvalue.isna().all() assert (table.score > 0).all() assert set(table.source).issubset(set(adata.obs['louvain'].astype(str))) ``` ## Inputs & Outputs Input is an annotated AnnData. builtin, LIANA, CellPhoneDB and CellChat use normalized X; NicheNet needs count-like data. PCA and neighbors are not required. The API returns an interaction DataFrame, with backend diagnostics and optional tables accessible through helpers. The CLI writes `processed.h5ad`, `report.md`, `result.json`, and `tables/lr_interactions.csv`, `top_interactions.csv`, `sender_receiver_summary.csv`, `group_role_summary.csv`, `pathway_summary.csv`. CellChat can add pathway, centrality, count and weight tables; CellPhoneDB can add means, p-values and significant means; NicheNet can add ligand activities and target links. Those tables and the corresponding figures are conditional. Backend exchange files are temporary. ## Flow 1. Load AnnData; preflight `--cell-type-key`, species, and per-method requirements (e.g., NicheNet needs `--receiver` / `--senders` / `--condition-*`). 2. Dispatch via `communicate` to the chosen backend (one of `builtin` / `liana` / `cellphonedb` / `cellchat_r` / `nichenet_r`). 3. Standardise the L-R table to columns `ligand`, `receptor`, `source`, `target`, `score`, `pvalue`, `pathway`. 4. Build sender-receiver / role / pathway summaries. 5. Detect "no interactions found" and print a UX-guardrail message; do NOT raise. 6. Save tables, figures, `processed.h5ad`, `report.md`, `result.json` (incl. `score_semantics` / `significance_semantics` / `pvalue_available`). ## Gotchas - `run_info(table)["fallback_used"]` is false: a missing selected backend raises; the API does not silently switch to builtin. - builtin scores are grouped ligand mean × receptor mean, not interaction probabilities. `pvalue` is NaN and `n_significant` is zero. - LIANA's `specificity_rank` is a consensus rank, not a p-value. `tables/lr_interactions.csv` retains that column, leaves `pvalue` NaN and reports zero significant interactions. Its default seed is 1337; the wrapper retains its existing species-independent resource selection. - CellPhoneDB is human-only and now passes seed 0 to `debug_seed`. `cellphonedb_lr(random_state=...)` changes it. Its v4.1.0 database downloads on first use to the user's cache; this is not an offline path unless cached. - CellChat and NicheNet need their R dependency stacks. NicheNet is human-only and also requires `lr_network_human_21122021.rds` and `weighted_networks_nsga2r_final.rds` under the user's `.cache/omicsclaw/nichenet/`; the wrapper does not download them. - Empty interaction tables are valid output. Check `run_info(table)["n_interactions_tested"]` before plotting or interpreting them. ## Key CLI ```bash # Demo (built-in annotated PBMC) python skills/singlecell/scrna/sc-cell-communication/sc_cell_communication.py --demo --output /tmp/sc_ccc_demo # Default builtin scorer (heuristic, no pvalue) python skills/singlecell/scrna/sc-cell-communication/sc_cell_communication.py \ --input annotated.h5ad --output results/ # LIANA rank aggregation (recommended general default) python skills/singlecell/scrna/sc-cell-communication/sc_cell_communication.py \ --input annotated.h5ad --output results/ --method liana # CellPhoneDB statistical (human only) python skills/singlecell/scrna/sc-cell-communication/sc_cell_communication.py \ --input annotated.h5ad --output results/ \ --method cellphonedb --cellphonedb-iterations 1000 --cellphonedb-threshold 0.1 # CellChat R workflow python skills/singlecell/scrna/sc-cell-communication/sc_cell_communication.py \ --input annotated.h5ad --output results/ \ --method cellchat_r --cellchat-prob-type triMean # NicheNet ligand prioritisation across conditions (human only) python skills/singlecell/scrna/sc-cell-communication/sc_cell_communication.py \ --input annotated.h5ad --output results/ \ --method nichenet_r \ --condition-key condition --condition-oi stim --condition-ref ctrl \ --receiver "Monocyte" --senders "T_cell,B_cell" --nichenet-top-ligands 20 ``` ## See also - `references/parameters.md` — every CLI flag, per-backend tunables - `references/methodology.md` — when each backend wins; species coverage - `references/output_contract.md` — `lr_interactions.csv` columns + `result.json` keys per backend - Adjacent skills: `sc-cell-annotation` (upstream — produces `obs["cell_type"]`), `sc-clustering` (upstream — provides leiden/louvain if you pass `--cell-type-key leiden`), `sc-grn` (parallel — TF→target regulatory networks, NOT L-R), `sc-differential-abundance` (parallel — cross-condition cell-state proportion changes) ## Dependencies Python packages this skill's script needs. They are not installed for you — check before a long run. `anndata`, `cellphonedb`, `liana`, `matplotlib`, `numpy`, `pandas`, `scanpy`, `scipy`, `seaborn` ## API ### `communicate(adata, *, method: str='builtin', cell_type_key: str='cell_type', species: str='human', cellphonedb_counts_data: str='hgnc_symbol', cellphonedb_iterations: int=1000, cellphonedb_threshold: float=0.1, cellphonedb_threads: int=4, cellphonedb_pvalue: float=0.05, cellchat_prob_type: str='triMean', cellchat_min_cells: int=10, condition_key: str | None=None, condition_oi: str | None=None, condition_ref: str | None=None, receiver: str | None=None, senders: list[str] | None=None, nichenet_top_ligands: int=20, nichenet_expression_pct: float=0.1, nichenet_lfc_cutoff: float=0.25, liana_random_state: int=1337, cellphonedb_random_state: int=0) -> pd.DataFrame` Return ranked ligand-receptor interactions without changing the input. builtin multiplies grouped ligand and receptor means and supplies no p-values. LIANA retains specificity_rank as a rank, not a significance statistic, and ignores species as in the existing wrapper. CellPhoneDB uses debug_seed=0 by default; its database may download on first use. R methods use temporary H5AD exchange files. NicheNet needs its two local resource files. Optional backend imports occur only when selected. Backend-specific tables and diagnostics are accessible through helpers. ### `builtin_lr(adata, *, cell_type_key: str='cell_type', species: str='human') -> pd.DataFrame` Return the curated mean-product heuristic; pvalue is always NaN. ### `liana_lr(adata, *, cell_type_key: str='cell_type', species: str='human', random_state: int=1337) -> pd.DataFrame` Return LIANA consensus scores and specificity ranks; neither is a p-value. ### `cellphonedb_lr(adata, *, cell_type_key: str='cell_type', species: str='human', counts_data: str='hgnc_symbol', iterations: int=1000, threshold: float=0.1, threads: int=4, pvalue: float=0.05, random_state: int=0) -> pd.DataFrame` Run CellPhoneDB permutations with an explicit debug_seed; database may download. ### `cellchat_lr(adata, *, cell_type_key: str='cell_type', species: str='human', prob_type: str='triMean', min_cells: int=10) -> pd.DataFrame` Run CellChat in R on normalized X; require the existing R dependency stack. ### `nichenet_ligands(adata, *, cell_type_key: str='cell_type', species: str='human', condition_key: str, condition_oi: str, condition_ref: str, receiver: str, senders: list[str], top_ligands: int=20, expression_pct: float=0.1, lfc_cutoff: float=0.25) -> pd.DataFrame` Run NicheNet and return LR scores; backend_tables includes ligand activities. ### `sender_receiver_summary(table: pd.DataFrame) -> pd.DataFrame` Return mean scores and interaction counts for each sender-receiver pair. ### `group_role_summary(table: pd.DataFrame) -> pd.DataFrame` Return summed incoming and outgoing interaction scores for each cell type. ### `pathway_summary(table: pd.DataFrame, *, pathways: pd.DataFrame | None=None) -> pd.DataFrame` Return mean pathway scores, using CellChat pathway results when supplied. ### `top_interactions(table: pd.DataFrame, *, n: int=50) -> pd.DataFrame` Return the first n interactions in the backend's existing ranked order. ### `backend_tables(table: pd.DataFrame) -> dict[str, pd.DataFrame]` Return copies of backend-specific tables, including optional R summaries. ### `interaction_heatmap_figure(table: pd.DataFrame)` Return a sender-by-receiver mean-score heatmap without writing files. ### `run_info(table: pd.DataFrame, *, keep: bool=True) -> dict` Return backend provenance; keep=False removes the table's run record.