CellChat — cell–cell communication¶
Precomputed CellChat outputs served as an interactive viewer: a source × target pathway heatmap, a sortable ligand-receptor table, and an optional cross-group delta view. Tool-callable from the copilot when both modules are enabled.
| extras_key | cellchat |
| config_key | cellchat |
| install | pip install 'stellar-atlas[cellchat]' |
| frontend tab | CellChat |
Enable¶
Input format¶
Four parquet files under source_dir/. STELLAR enforces the columns
below and ignores extras — pass-through columns are preserved verbatim
in the parquet copies but are not surfaced by the routes.
cellchat_groups.parquet — one row per CellChat run group¶
| column | type | required | notes |
|---|---|---|---|
group_id |
string | yes | Unique slug — short, URL-safe (e.g. subtype:t1). |
label |
string | yes | Human-readable name shown in the SPA picker. |
n_cells |
int64 | no | Cell count the CellChat object was built from. |
A "group" is one cohort whose signalling you ran in R. STELLAR is agnostic to what defines the cohort — you might key by subtype, by sex, by condition, by donor. Use whatever slug makes sense.
cellchat_celltypes.parquet — which cell types each group exercised¶
| column | type | required |
|---|---|---|
group_id |
string | yes |
cell_type |
string | yes |
cellchat_pathway_strength.parquet — pathway × source × target strength¶
| column | type | required |
|---|---|---|
group_id |
string | yes |
pathway |
string | yes |
source |
string | yes |
target |
string | yes |
prob |
float32 | yes |
The prob value is the communication probability CellChat assigned to
that edge for that pathway. Drive the source × target heatmap directly
from this table.
cellchat_lr.parquet — ligand-receptor hits¶
| column | type | required |
|---|---|---|
group_id |
string | yes |
pathway |
string | yes |
ligand |
string | yes |
receptor |
string | yes |
source |
string | yes |
target |
string | yes |
prob |
float32 | yes |
padj |
float64 | yes |
Producing the input from a CellChat .rds¶
CellChat is an R-only package — STELLAR does not depend on it directly and stays Python-pure. Run the R extractor below once per atlas, preferably as part of your existing R pipeline.
Where to put this
Save as extract_cellchat.R next to your .rds files; STELLAR
intentionally doesn't ship the script (you'll want to tweak the
field mapping for your run anyway).
# extract_cellchat.R
# Convert a list of CellChat objects (one per group) into the four
# parquet files STELLAR's cellchat module reads.
#
# Rscript extract_cellchat.R \
# --out data/external/cellchat \
# --rds ccs_by_group.rds # named list of CellChat objects
suppressPackageStartupMessages({
library(CellChat)
library(arrow)
library(dplyr)
library(tibble)
})
args <- commandArgs(trailingOnly = TRUE)
out <- args[which(args == "--out") + 1]
rds <- args[which(args == "--rds") + 1]
dir.create(out, recursive = TRUE, showWarnings = FALSE)
ccs <- readRDS(rds) # list(group_id = CellChat-object, ...)
groups_rows <- list()
celltype_rows <- list()
pathway_rows <- list()
lr_rows <- list()
for (gid in names(ccs)) {
cc <- ccs[[gid]]
cell_types <- levels(cc@idents)
groups_rows[[gid]] <- tibble(
group_id = gid,
label = gid,
n_cells = ncol(cc@data)
)
celltype_rows[[gid]] <- tibble(
group_id = gid,
cell_type = cell_types
)
prob_arr <- cc@netP$prob
for (pwy in dimnames(prob_arr)[[3]]) {
m <- prob_arr[, , pwy]
df <- as.data.frame.table(m, stringsAsFactors = FALSE)
colnames(df) <- c("source", "target", "prob")
df <- df[df$prob > 0, , drop = FALSE]
if (nrow(df)) {
pathway_rows[[paste(gid, pwy, sep = "::")]] <- tibble(
group_id = gid,
pathway = pwy,
source = df$source,
target = df$target,
prob = as.numeric(df$prob)
)
}
}
lr <- subsetCommunication(cc)
if (nrow(lr)) {
lr_rows[[gid]] <- tibble(
group_id = gid,
pathway = lr$pathway_name,
ligand = lr$ligand,
receptor = lr$receptor,
source = as.character(lr$source),
target = as.character(lr$target),
prob = as.numeric(lr$prob),
padj = as.numeric(lr$pval)
)
}
}
write_parquet(bind_rows(groups_rows), file.path(out, "cellchat_groups.parquet"))
write_parquet(bind_rows(celltype_rows), file.path(out, "cellchat_celltypes.parquet"))
write_parquet(bind_rows(pathway_rows), file.path(out, "cellchat_pathway_strength.parquet"))
write_parquet(bind_rows(lr_rows), file.path(out, "cellchat_lr.parquet"))
message("wrote 4 parquet files to ", out)
If your pval is uncorrected, run p.adjust() per group/pathway
before emitting the padj column.
API surface¶
| route | what |
|---|---|
GET /api/cellchat/groups |
list groups + cell-type counts |
GET /api/cellchat/pathways/{group_id} |
pathways with summed prob |
POST /api/cellchat/network |
body {group_id, pathway?, top_n?} — Arrow IPC of (source, target, pathway, prob) |
POST /api/cellchat/lr |
body {group_id, pathway, top_n?, padj_max?} — Arrow IPC of the L-R table |
POST /api/cellchat/compare |
body {group_a, group_b, pathway?, top_n?} — Arrow IPC of (pathway, source, target, prob_a, prob_b, delta) |
Example calls¶
List every CellChat group + its cell types:
curl -s http://localhost:18901/api/cellchat/groups | python -m json.tool
# {"groups": [{"group_id": "condition:control",
# "label": "Control",
# "n_cells": 12450,
# "cell_types": ["T", "B", "Neuron", ...]}, ...]}
Top signaling pathways for one group:
curl -s 'http://localhost:18901/api/cellchat/pathways/condition:control?top_n=10' \
| python -m json.tool
# {"pathways": [{"pathway": "TGFb",
# "total_prob": 4.17,
# "n_pairs": 36}, ...]}
Network for one group + pathway (Arrow IPC binary):
curl -sX POST http://localhost:18901/api/cellchat/network \
-H 'content-type: application/json' \
-d '{"group_id": "condition:control",
"pathway": "TGFb",
"top_n": 50}' \
-o /tmp/cc_network.arrow
Copilot tools¶
When both cellchat and copilot are enabled the module contributes
three tools to the Claude agent loop.
list_cellchat_groups¶
{
"name": "list_cellchat_groups",
"description": "List every CellChat run group, with the cell types each group exercised.",
"input_schema": {"type": "object", "properties": {}, "required": []}
}
get_cellchat_pathways¶
{
"name": "get_cellchat_pathways",
"description": "List signaling pathways for one group, aggregated across source × target cell-type pairs.",
"input_schema": {
"type": "object",
"properties": {
"group_id": {"type": "string"},
"top_n": {"type": "integer", "default": 25}
},
"required": ["group_id"]
}
}
compare_cellchat_groups¶
{
"name": "compare_cellchat_groups",
"description": "Compare two groups' signaling — returns the largest source × target × pathway deltas (prob_a - prob_b).",
"input_schema": {
"type": "object",
"properties": {
"group_id_a": {"type": "string"},
"group_id_b": {"type": "string"},
"pathway": {"type": "string"},
"top_n": {"type": "integer", "default": 25}
},
"required": ["group_id_a", "group_id_b"]
}
}
System prompt fragment¶
CellChat signaling is precomputed per group (a 'group' is one cohort: subtype / sex / condition slice). Discover group_ids with
list_cellchat_groups, then drill down withget_cellchat_pathwaysorcompare_cellchat_groups. Probabilities are CellChat's communication probability.
Implementation at stellar/modules/cellchat/__init__.py — see
Extending for the pattern.
Frontend tab¶
The CellChat tab appears in the SPA nav when this module is enabled:
group picker (radio buttons) → pathway dropdown → source × target
heatmap of prob → sortable L-R table beneath. Toggle "compare with
another group" to swap the main heatmap for a prob_a − prob_b delta
heatmap.