Skip to contents

Overview

Differential expression tells you which genes change; upstream regulator analysis (URA) asks which transcription factors drive those changes. RunURA() infers per-sample TF activity from gene expression using decoupleR and the CollecTRI TF-target network, then (optionally) tests for differential TF activity between conditions.

The workflow:

  1. Collapse cells to pseudobulk profiles (per group, donor, and any stratifier) - statistically sound replicates for downstream testing.
  2. Score TF activity per sample with decoupleR’s univariate linear model (ulm, the default; viper and mlm are also available) against CollecTRI.
  3. If a contrast is given, test for differential activity (limma when there are at least 3 donors per side, t-test otherwise).
  4. Save a TF-activity heatmap and a differential bubble plot, each with a .legend sidecar.

RunURA() complements the pathway tools (Vignettes 08-10): GSEA finds enriched gene sets, URA finds the regulators upstream of them.

decoupleR is an optional dependency. Install it once (Section 1). The CollecTRI network is fetched from OmniPath on first use and cached locally.


1. Installation

if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager")
BiocManager::install("decoupleR")
# Optional but recommended for the differential heatmap and limma-based testing:
BiocManager::install(c("limma", "ComplexHeatmap"))

The first RunURA() call downloads CollecTRI from OmniPath and caches it to ~/.scSidekick/collectri_<organism>.rds, so subsequent runs are offline and instant. See the note on network access at the end if OmniPath is blocked on your network.


2. Basic TF activity (no contrast)

With just group.by, RunURA() infers mean TF activity per group and saves a z-scored activity heatmap of the most variable TFs.

ura <- RunURA(obj,
  group.by   = "CellType",
  organism   = "human",
  output_dir = "./Figures/URA")

The return value is a list:

Element Description
activity TF x group mean-activity matrix
scores Long-format decoupleR output (all pseudobulk samples)
de Differential table (NULL when no contrast)
network The TF-target network actually used
ura$activity[1:5, 1:4]      # TFs x cell types

3. Differential TF activity between conditions

Supply contrast.by (the condition column) and contrast (a c("test", "reference") pair), plus donor.by to enable replicate-based testing. This produces a differential heatmap (activity difference, with dots marking p.adj < 0.05) and a bubble plot.

ura <- RunURA(obj,
  group.by    = "CellType",
  donor.by    = "Donor.ID",            # biological replicates -> limma
  contrast.by = "Diagnosis",
  contrast    = c("AD", "Control"),    # test vs reference
  organism    = "human",
  output_dir  = "./Figures/URA")

head(ura$de)   # TF, cell_type, activity_diff, p.value, p.adj, contrast ...

Multiple contrasts at once

ura <- RunURA(obj,
  group.by    = "CellType",
  donor.by    = "Donor.ID",
  contrast.by = "Diagnosis",
  contrast    = list(c("AD", "Control"), c("MCI", "Control")),
  output_dir  = "./Figures/URA")

Stratify with split.by

split.by (e.g. "Sex") adds a second dimension: the contrast is computed within each level, columns of the heatmap are grouped by it, and the bubble plot is faceted.

ura <- RunURA(obj,
  group.by    = "CellType",
  donor.by    = "Donor.ID",
  split.by    = "Sex",
  contrast.by = "Diagnosis",
  contrast    = c("AD", "Control"),
  output_dir  = "./Figures/URA")

4. Useful options

RunURA(obj,
  group.by       = "CellType",
  contrast.by    = "Diagnosis",
  contrast       = c("AD", "Control"),
  method         = "ulm",        # "ulm" (default), "viper", or "mlm"
  n_top          = 30,           # TFs shown in plots
  min_cells      = 10,           # minimum cells per pseudobulk group
  group.levels   = c("Microglia", "Astrocyte"),  # restrict to some groups
  use_pseudobulk = TRUE,         # FALSE = score per cell, then average
  organism       = "human",      # or "mouse"
  output_dir     = "./Figures/URA")
  • use_pseudobulk = TRUE (default) is the rigorous route: TF activity is inferred from per-sample pseudobulk averages. With use_pseudobulk = FALSE, decoupleR scores every cell directly and activities are averaged per group (simpler, no donor.by needed, but cells are pseudoreplicates so interpret DE p-values with caution; use max_cells to subsample large objects).
  • force = TRUE recomputes pseudobulk even if a cached result is stored in obj@misc$pseudobulk.
  • A custom network (a data frame with source, target, mor) can be passed instead of CollecTRI.

5. Outputs

When output_dir is set, RunURA() writes (file names encode group.by, split.by, and the contrast):

  • <object>_URA_by_<group>..._heatmap.pdf - one per contrast; rows are top TFs, columns are cell types (grouped by split.by), color is activity difference, black dots mark p.adj < 0.05.
  • <object>_URA_by_<group>..._bubble.pdf - matrix bubble plot (cell type x TF, color = activity difference, size = -log10 p.adj), faceted by contrast and split.by.
  • A .legend sidecar next to each PDF, so create_analysis_pptx() picks the description up automatically.

6. Note on network access (OmniPath)

CollecTRI is fetched from the OmniPath web service on first use. RunURA() tries several fallbacks (bundled copy, alternate OmniPathR options, disk cache, direct REST download) and caches a working network to ~/.scSidekick/collectri_<organism>.rds. If your institution blocks OmniPath, fetch the network once from an unrestricted machine and drop it in the cache:

net <- decoupleR::get_collectri(organism = "human")
saveRDS(net, "~/.scSidekick/collectri_human.rds")

Then every RunURA() call uses the cached network offline. You can also pass it directly via the network argument.


See also

  • Vignette 08-10: RunGSEA() and the pathway family - the enriched gene sets that URA finds the regulators for
  • Vignette 14: ComputePseudobulk() - the donor-level aggregation URA builds on
  • Vignette 12: create_analysis_pptx() - assembles the URA figures (with their .legend text) into a deck

Session info

## R version 4.3.1 (2023-06-16)
## Platform: aarch64-apple-darwin20 (64-bit)
## Running under: macOS 15.6
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: America/Chicago
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.37     desc_1.4.3        R6_2.6.1          fastmap_1.2.0    
##  [5] xfun_0.52         cachem_1.1.0      knitr_1.45        htmltools_0.5.8.1
##  [9] rmarkdown_2.29    lifecycle_1.0.4   cli_3.6.5         sass_0.4.10      
## [13] pkgdown_2.1.3     textshaping_0.3.7 jquerylib_0.1.4   systemfonts_1.3.2
## [17] compiler_4.3.1    rstudioapi_0.16.0 tools_4.3.1       ragg_1.4.0       
## [21] bslib_0.9.0       evaluate_0.23     yaml_2.3.10       jsonlite_2.0.0   
## [25] rlang_1.1.6       fs_1.6.6          htmlwidgets_1.6.4