-
Notifications
You must be signed in to change notification settings - Fork 101
Description
Hi,
I’ve been following the tutorial “Comparison analysis of multiple datasets using CellChat” to compare two conditions. While the workflow runs fine, I find the default bubble plots difficult to interpret, especially since the same ligand–receptor pairs sometimes appear in both up- and downregulated pathway categories.
To obtain a clearer directional interpretation, I implemented the following approach:
- Extracts communication probabilities from each condition.
- Computes Δ probability (Condition2 − Condition1) for each ligand–receptor pair.
- Filters interactions that are either significant in at least one condition (pval.Condition1 < 0.05 | pval.Condition2 < 0.05).
- Further restricts to interactions where the ligand or receptor is a DEG between conditions (ligand.pvalues < 0.05 | receptor.pvalues < 0.05).
- Visualizes Δ probability with dot plots (color = direction, size = magnitude).
Would this be an appropriate way to analyze and visualize differential communication from CellChat outputs, or would you recommend a different strategy? I’d also like to ask specifically whether the thresholds I chose for communication p-values and DEGs are appropriate, or if you would recommend alternative settings or additional filters to better capture meaningful differential interactions.
Thanks in advance for your guidance!
cellchat.Condition1 <- readRDS('cellchat_Condition1_Processed.rds')
cellchat.Condition2 <- readRDS('cellchat_Condition2_Processed.rds')
object.list <- list(Condition1 = cellchat.Condition1, Condition2 = cellchat.Condition2)
cellchat` <- mergeCellChat(object.list, add.names = names(object.list),cell.prefix = TRUE)
pos.dataset = "Condition2"
features.name = paste0(pos.dataset, ".merged")
cellchat <- identifyOverExpressedGenes(
cellchat,
group.dataset = "datasets",
pos.dataset = pos.dataset,
features.name = features.name,
only.pos = FALSE,
thresh.pc = 0.1,
thresh.fc = 0.05,
thresh.p = 0.05,
group.DE.combined = FALSE
)net <- netMappingDEG(cellchat, features.name = features.name, variable.all = TRUE)
net.up <- subsetCommunication(cellchat, net = net, datasets = "Condition1", ligand.logFC = 0.05, receptor.logFC = NULL)
net.down <- subsetCommunication(cellchat, net = net, datasets = "Condition2", ligand.logFC = -0.05, receptor.logFC = NULL)
cells.use <- c("CellType1", "CellType2", "CellType3", ...)
pathways.interest <- c("Pathway1", "Pathway2", "Pathway3", ...)
df_Condition1 <- subsetCommunication(cellchat.Condition1,
sources.use = cells.use,
targets.use = cells.use,
signaling = pathways.interest)
df_Condition2 <- subsetCommunication(cellchat.Condition2,
sources.use = cells.use,
targets.use = cells.use,
signaling = pathways.interest)
df_diff <- df_Condition2 %>%
rename(prob.Condition2 = prob, pval.Condition2 = pval) %>%
inner_join(
df_Condition1 %>%
rename(prob.Condition1 = prob, pval.Condition1 = pval),
by = c("source", "target", "interaction_name", "pathway_name")
) %>%
mutate(
delta_prob = prob.Condition2 - prob.Condition1,
abs_delta = abs(delta_prob),
significant_prob = (pval.Condition2 < 0.05 | pval.Condition1 < 0.05)
) %>%
filter(significant_prob, abs_delta > 0.01)
df_pathway <- df_diff %>%
group_by(pathway_name) %>%
summarise(mean_delta = mean(delta_prob), .groups = "drop")
# Pathway-level net direction plot
ggplot(df_pathway,
aes(x = reorder(pathway_name, mean_delta),
y = mean_delta,
fill = mean_delta > 0)) +
geom_col() +
coord_flip() +
scale_fill_manual(values = c("#2166AC", "#B2182B"), guide = "none") +
theme_classic(base_size = 14) +
labs(title = "Net Direction of Inflammatory Pathways",
subtitle = "Mean Δ Probability (Condition2 vs Condition1)",
x = "Pathway",
y = "Mean Δ Communication")
net.sub <- subsetCommunication(cellchat,
net = net,
sources.use = cells.use,
targets.use = cells.use,
signaling = pathways.interest) %>%
select(source, target, interaction_name,
ligand.pvalues, receptor.pvalues)
df_diff <- df_diff %>%
inner_join(net.sub,
by = c("source", "target", "interaction_name")) %>%
mutate(DEG_significant =
(ligand.pvalues < 0.05 | receptor.pvalues < 0.05)) %>%
filter(DEG_significant)
df_plot <- df_diff %>%
mutate(direction = ifelse(delta_prob > 0,
"Up in Condition2",
"Down in Condition2"))
gg_final <- ggplot(df_plot,
aes(x = source_target,
y = interaction_name_clean)) +
geom_point(aes(size = abs_delta,
fill = direction),
shape = 21,
color = "black",
stroke = 0.4) +
scale_fill_manual(values = c("Up in Condition2" = "#B2182B",
"Down in Condition2" = "#2166AC"),
name = "Direction of Δ") +
scale_size_continuous(range = c(4, 12),
name = "Magnitude |Δ Prob|") +
theme_bw(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45,
hjust = 1,
face = "bold"),
axis.text.y = element_text(size = 9,
face = "italic"),
panel.grid.major = element_line(color = "grey95"),
legend.title = element_text(face = "bold")
) +
labs(title = "Differential Cell–Cell Signaling",
subtitle = paste("Celltypes of Interest Axis | Pathways:",
paste(pathways.interest,
collapse = ", ")),
x = "Sender → Receiver",
y = "Ligand–Receptor Interaction")

