Differential connectivity analysis enables comparison of cell-cell communication networks between conditions (e.g., disease vs. healthy, treated vs. control). This vignette demonstrates the complete workflow for identifying altered signaling pathways.
┌─────────────────┐ ┌─────────────────┐
│ Condition 1 │ │ Condition 2 │
│ (Reference) │ │ (Test) │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│CreateConnectome │ │CreateConnectome │
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
▼
┌───────────────────────┐
│DifferentialConnectome │
└───────────┬───────────┘
│
┌───────────┴───────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Visualization │ │ Statistics │
└─────────────────┘ └─────────────────┘
library(Seurat)
library(Connectome)
# Method 1: SplitObject
seurat_list <- SplitObject(seurat_obj, split.by = "condition")
seurat_ctrl <- seurat_list[["control"]]
seurat_treat <- seurat_list[["treatment"]]
# Method 2: EvenSplit (balanced sampling)
seurat_list <- EvenSplit(seurat_obj, split.by = "condition")
seurat_ctrl <- seurat_list[["control"]]
seurat_treat <- seurat_list[["treatment"]]EvenSplit() ensures each cell type has equal
representation across conditions, preventing bias from unequal cell
numbers:
# Reference connectome (control)
conn_ctrl <- CreateConnectome(
object = seurat_ctrl,
species = "human",
LR.database = "fantom5",
min.cells.per.ident = 30,
p.values = FALSE # Optional for differential analysis
)
# Test connectome (treatment)
conn_treat <- CreateConnectome(
object = seurat_treat,
species = "human",
LR.database = "fantom5",
min.cells.per.ident = 30,
p.values = FALSE
)Important: Both connectomes must have: - Same cell type identities - Same ligand-receptor pairs - Matching edge identifiers
diff_conn <- DifferentialConnectome(
connect.ref = conn_ctrl,
connect.test = conn_treat,
min.pct = 0.1
)| Column | Description |
|---|---|
ligand.norm.lfc |
Log2 fold change of ligand expression |
recept.norm.lfc |
Log2 fold change of receptor expression |
weight.norm.lfc |
Log2 fold change of edge weight |
pct.source.1 |
% source cells expressing ligand (reference) |
pct.source.2 |
% source cells expressing ligand (test) |
pct.target.1 |
% target cells expressing receptor (reference) |
pct.target.2 |
% target cells expressing receptor (test) |
score |
Perturbation score = |
The score captures edges where both ligand and receptor are differentially expressed:
\[\text{Score} = |\log_2(\text{FC}_L)| \times |\log_2(\text{FC}_R)|\]
# Top perturbed edges
top_edges <- diff_conn[order(-diff_conn$score), ][1:20, ]
# Upregulated signaling (both components increased)
upregulated <- subset(diff_conn,
ligand.norm.lfc > 0 & recept.norm.lfc > 0 & score > 1)
# Downregulated signaling (both components decreased)
downregulated <- subset(diff_conn,
ligand.norm.lfc < 0 & recept.norm.lfc < 0 & score > 1)
# Rewired signaling (opposite direction changes)
rewired <- subset(diff_conn,
(ligand.norm.lfc > 0 & recept.norm.lfc < 0) |
(ligand.norm.lfc < 0 & recept.norm.lfc > 0))# Unaligned view (separate ligand/receptor panels)
DifferentialScoringPlot(
diff_conn,
min.score = 0.5,
aligned = FALSE
)
# Aligned view (edge-matched)
DifferentialScoringPlot(
diff_conn,
sources.include = c("Fibroblast", "Macrophage"),
targets.include = c("Epithelial", "Endothelial"),
min.score = 0.5,
aligned = TRUE
)# Analyze by signaling mode
modes <- unique(diff_conn$mode)
mode_summary <- data.frame()
for (m in modes) {
subset_m <- diff_conn[diff_conn$mode == m, ]
mode_summary <- rbind(mode_summary, data.frame(
mode = m,
n_edges = nrow(subset_m),
mean_score = mean(subset_m$score, na.rm = TRUE),
max_score = max(subset_m$score, na.rm = TRUE),
n_upregulated = sum(subset_m$ligand.norm.lfc > 0 &
subset_m$recept.norm.lfc > 0, na.rm = TRUE),
n_downregulated = sum(subset_m$ligand.norm.lfc < 0 &
subset_m$recept.norm.lfc < 0, na.rm = TRUE)
))
}
# Sort by mean perturbation score
mode_summary <- mode_summary[order(-mode_summary$mean_score), ]
head(mode_summary, 10)# Identify most affected cell types (as senders)
sender_changes <- aggregate(score ~ source, data = diff_conn,
FUN = function(x) c(mean = mean(x), max = max(x)))
# Identify most affected cell types (as receivers)
receiver_changes <- aggregate(score ~ target, data = diff_conn,
FUN = function(x) c(mean = mean(x), max = max(x)))# Export significant differential edges
sig_edges <- subset(diff_conn, score > 1)
write.csv(sig_edges, "differential_edges.csv", row.names = FALSE)
# Export for Cytoscape
cytoscape_format <- diff_conn[, c("source", "target", "ligand", "receptor",
"ligand.norm.lfc", "recept.norm.lfc", "score")]
write.csv(cytoscape_format, "cytoscape_import.csv", row.names = FALSE)EvenSplit() for balanced comparisons# Stringent: High confidence changes
high_conf <- subset(diff_conn,
score > 2 &
(pct.source.1 > 0.1 | pct.source.2 > 0.1) &
(pct.target.1 > 0.1 | pct.target.2 > 0.1)
)
# Discovery: Explore all potential changes
discovery <- subset(diff_conn,
score > 0.5 &
(pct.source.1 > 0.05 | pct.source.2 > 0.05)
)sessionInfo()
#> R version 4.6.0 (2026-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] rmarkdown_2.31
#>
#> loaded via a namespace (and not attached):
#> [1] digest_0.6.39 R6_2.6.1 fastmap_1.2.0 xfun_0.59
#> [5] maketools_1.3.2 cachem_1.1.0 knitr_1.51 htmltools_0.5.9
#> [9] buildtools_1.0.0 lifecycle_1.0.5 cli_3.6.6 sass_0.4.10
#> [13] jquerylib_0.1.4 compiler_4.6.0 sys_3.4.3 tools_4.6.0
#> [17] evaluate_1.0.5 bslib_0.11.0 yaml_2.3.12 otel_0.2.0
#> [21] jsonlite_2.0.0 rlang_1.2.0