SCENIC tutorial
TL;DR: SCENIC infers Transcription Factor (TF) activity from single-cell data. Use pySCENIC for Python/CLI workflows. Key steps: GRN (co-expression), CTX (motif enrichment), and AUCell (activity scoring).
What is SCENIC?
Single-Cell rEgulatory Network Inference and Clustering.
Most scRNA-seq analysis looks at gene expression (mRNA levels). SCENIC looks at regulon activity (is the TF and its targets active?).
Why use it?
- Identify master regulators driving cell states.
- Robust to dropouts (aggregates signal across target genes).
- Works across species (Human, Mouse, Fly).

Part 1: Installation (The Right Way)
pySCENIC dependency management can be tricky. To avoid common errors (like np.object deprecation), use this exact conda setup:
# Create environment
conda create -y -n pyscenic python=3.10
# Activate
conda activate pyscenic
# Install main packages
pip install pyscenic scanpy loompy matplotlib seaborn
# Critical dependency fixes
pip install "numpy<1.24" "pandas<2.0"
Verify installation:
pyscenic --help
Part 2: Prepare Input Data
1. Expression Matrix (.loom)
Export your Seurat/Scanpy object to .loom.
From Seurat (R):
library(Seurat)
library(SeuratDisk)
# Load object
obj <- readRDS("seurat_obj.rds")
# Convert to loom
as.loom(obj, filename = "input.loom", verbose = FALSE)
From Scanpy (Python):
import scanpy as sc
adata = sc.read("adata.h5ad")
adata.write_loom("input.loom")
2. Download Databases
You need three types of files from cisTarget resources:
A. TF List:
wget https://resources.aertslab.org/cistarget/tf_lists/allTFs_hg38.txt
B. Motif Rankings (Gene-based):
Warning: Large files (1GB+)
# Human 10kb up/down
wget https://resources.aertslab.org/cistarget/databases/homo_sapiens/hg38/refseq_r80/mc_v10_clust/gene_based/hg38_10kbp_up_10kbp_down_full_tx_v10_clust.genes_vs_motifs.rankings.feather
C. Motif Annotation:
wget https://resources.aertslab.org/cistarget/motif2tf/motifs-v10nr_clust-nr.hgnc-m0.001-o0.0.tbl
Part 3: Running the Pipeline (CLI)
Run these steps in a screen or tmux session (takes hours).
Step 1: GRN (Gene Regulatory Network)
Infers co-expression modules.
pyscenic grn \
--num_workers 12 \
--output adj.tsv \
--method grnboost2 \
input.loom \
allTFs_hg38.txt
Step 2: CTX (Cis-Target enrichment)
Prunes modules for direct targets (motif enrichment).
pyscenic ctx \
adj.tsv \
hg38_10kbp_up_10kbp_down_full_tx_v10_clust.genes_vs_motifs.rankings.feather \
--annotations_fname motifs-v10nr_clust-nr.hgnc-m0.001-o0.0.tbl \
--expression_mtx_fname input.loom \
--output regulons.csv \
--mask_dropouts \
--num_workers 12
Step 3: AUCell (Activity Scoring)
Scores regulon activity in each cell.
pyscenic aucell \
input.loom \
regulons.csv \
--output output_scenic.loom \
--num_workers 12
Part 4: Visualization in Python
Load the results into Scanpy for visualization.
import scanpy as sc
import loompy as lp
import pandas as pd
# Load original data
adata = sc.read_loom("input.loom")
# Load SCENIC results (AUCell matrix)
lf = lp.connect("output_scenic.loom", mode='r', validate=False)
auc_mtx = pd.DataFrame(lf.ca.RegulonsAUC, index=lf.ca.CellID)
lf.close()
# Add to AnnData
adata.obsm['X_aucell'] = auc_mtx
1. UMAP on Regulons
Cluster cells based on TF activity rather than gene expression.
# Compute neighbors on AUCell matrix
sc.pp.neighbors(adata, use_rep='X_aucell')
sc.tl.umap(adata)
sc.pl.umap(adata, color='celltype', title='Regulon-based UMAP')
2. Visualize Specific TFs
# Copy specific regulon scores to .obs for plotting
regulon = 'PAX5(+)'
adata.obs[regulon] = auc_mtx[regulon]
sc.pl.umap(adata, color=regulon, cmap='viridis')
3. Regulon Specificity
# Matrix plot of top regulons per cluster
sc.pl.matrixplot(adata,
var_names=['PAX5(+)', 'EBF1(+)', 'TCF3(+)'],
groupby='celltype',
use_raw=False)
Troubleshooting
"AttributeError: module 'numpy' has no attribute 'object'"
Cause: NumPy 1.24+ deprecated np.object.
Fix: Downgrade NumPy:
pip install "numpy<1.24"
"TypeError: Must supply at least one delayed object"
Cause: dask version incompatibility.
Fix:
pip install dask==2023.5.0 distributed==2023.5.0
"loompy.connect validate=False error"
Cause: Newer loompy versions validate heavily.
Fix: Regenerate the loom file using scanpy instead of SeuratDisk if possible, or use the validate=False flag in python scripts.
Resources
- Official Docs: scenic.aertslab.org
- pySCENIC Repo: github.com/aertslab/pySCENIC
- Nature Protocol: Aibar et al. (2017)
See also:
Originally posted on WeChat Official Account: bioinfo_studyroom