- Notifications
You must be signed in to change notification settings - Fork 102
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") 
