3 Advanced workflow with MultiAssayExperiment

In this chapter, we illustrate the epiregulon workflow starting from data in the form of SingleCellExperiment objects using the Wilcoxon weight estimation method.

This is a dataset of hematopoiesis from the ArchR tutorial. Prior to using epiregulon, this dataset has been fully preprocessed in ArchR, and converted to a MultiAssayExperiment using epireglon.archr::archr2MAE. The MAE object was uploaded to scMultiome for full reproducibility. In this dataset, scRNAseq and scATACseq were unpaired and integrated by the ArchR::addGeneIntegrationMatrix function.

3.1 Data preparation

Download the example dataset from scMultiome package

mae <- scMultiome::hematopoiesis()

# Load peak matrix
PeakMatrix <- mae[["PeakMatrix"]]

# Load expression matrix
GeneExpressionMatrix <- mae[["GeneIntegrationMatrix"]]

# Add gene symbols to rownames
rownames(GeneExpressionMatrix) <- rowData(GeneExpressionMatrix)$name

# Transfer dimensionality reduction matrix to GeneExpression
reducedDim(GeneExpressionMatrix, "IterativeLSI") <- 
  reducedDim(mae[['TileMatrix500']], "IterativeLSI")
reducedDim(GeneExpressionMatrix, "UMAP") <- 
  reducedDim(mae[['TileMatrix500']], "UMAP")

Visualize the data

scater::plotReducedDim(GeneExpressionMatrix, 
                       dimred = "UMAP", 
                       text_by = "Clusters2", 
                       colour_by = "Clusters2",
                       point_size = 0.3,
                       point_alpha = 0.3)

3.2 Retrieve bulk TF ChIP-seq binding sites

First, we retrieve the information of TF binding sites collected from Cistrome and ENCODE ChIP-seq. Currently, human genomes hg19 and hg38 and mouse genome mm10 are available

library(epiregulon)
grl <- getTFMotifInfo(genome = "hg19")
## snapshotDate(): 2024-03-11
## see ?scMultiome and browseVignettes('scMultiome') for documentation
## loading from cache
grl
## GRangesList object of length 1558:
## $AEBP2
## GRanges object with 2761 ranges and 0 metadata columns:
##          seqnames            ranges strand
##             <Rle>         <IRanges>  <Rle>
##      [1]     chr1       10001-10446      *
##      [2]     chr1     877485-877780      *
##      [3]     chr1     919866-920161      *
##      [4]     chr1   2985496-2985846      *
##      [5]     chr1   2985975-2986514      *
##      ...      ...               ...    ...
##   [2757]     chrY   8333302-8333771      *
##   [2758]     chrY 13842450-13842966      *
##   [2759]     chrY 13868154-13868670      *
##   [2760]     chrY 21464547-21465020      *
##   [2761]     chrY 22147548-22147868      *
##   -------
##   seqinfo: 25 sequences from an unspecified genome; no seqlengths
## 
## ...
## <1557 more elements>

3.4 Add TF motif binding to peaks

The next step is to add the TF motif binding information by overlapping the regions of the peak matrix with the bulk chip-seq database.

overlap <- addTFMotifInfo(grl = grl, p2g = p2g, peakMatrix = PeakMatrix)
## Computing overlap...
## Success!
head(overlap)
##      idxATAC idxTF      tf
## 1013       7    13    ARNT
## 1014       7    17    ATF2
## 1015       7    21    ATF7
## 1016       7    28    BCL6
## 1017       7    30    BCOR
## 1018       7    32 BHLHE40

3.5 Generate regulons

A long format dataframe, representing the inferred regulons, is then generated. The dataframe consists of three columns:

  • tf (transcription factor)
  • target gene
  • peak to gene correlation between tf and target gene
regulon <- getRegulon(p2g, overlap, aggregate=FALSE)
regulon
## DataFrame with 1712540 rows and 10 columns
##           idxATAC         chr     start       end    idxRNA      target  distance     idxTF              tf     corr
##         <integer> <character> <integer> <integer> <integer> <character> <integer> <integer>     <character> <matrix>
## 1               7        chr1    801002    801502         2   LINC00115     36099        13            ARNT 0.627327
## 2               7        chr1    801002    801502         2   LINC00115     36099        17            ATF2 0.627327
## 3               7        chr1    801002    801502         2   LINC00115     36099        21            ATF7 0.627327
## 4               7        chr1    801002    801502         2   LINC00115     36099        28            BCL6 0.627327
## 5               7        chr1    801002    801502         2   LINC00115     36099        30            BCOR 0.627327
## ...           ...         ...       ...       ...       ...         ...       ...       ...             ...      ...
## 1712536    146412       chr22  51110826  51111326     12090      SHANK3         0      1172 POLR2AphosphoS5 0.532273
## 1712537    146412       chr22  51110826  51111326     12090      SHANK3         0      1312           ZNF16 0.532273
## 1712538    146412       chr22  51110826  51111326     12090      SHANK3         0      1461          ZNF600 0.532273
## 1712539    146412       chr22  51110826  51111326     12090      SHANK3         0      1490          ZNF687 0.532273
## 1712540    146412       chr22  51110826  51111326     12090      SHANK3         0      1520          ZNF777 0.532273

3.6 Prune network

Epiregulon prunes the network by performing tests of independence on the observed number of cells jointly expressing transcription factor (TF), regulatory element (RE) and target gene (TG) vs the expected number of cells if TF/RE and TG are independently expressed. We implement two tests, the binomial test and the chi-square test. In the binomial test, the expected probability is P(TF, RE) * P(TG), and the number of trials is the total number of cells, and the observed successes is the number of cells jointly expressing all three elements. In the chi-square test, the expected probability for having all 3 elements active is also P(TF, RE) * P(TG) and the probability otherwise is 1- P(TF, RE) * P(TG). The observed cell count for the active category is the number of cells jointly expressing all three elements, and the cell count for the inactive category is n - n_triple.

We calculate cluster-specific p-values if users supply cluster labels. This is useful if we are interested in cluster-specific networks. The pruned regulons can then be used to visualize differential networks for transcription factors of interest. See section on differential networks.

pruned.regulon <- pruneRegulon(expMatrix = GeneExpressionMatrix,
                               exp_assay = "normalizedCounts",
                               peakMatrix = PeakMatrix,
                               peak_assay = "counts",
                               regulon = regulon,
                               prune_value = "pval",
                               regulon_cutoff = 0.05,
                               clusters = GeneExpressionMatrix$Clusters2)
## pruning network with chi.sq tests using a regulon cutoff of pval<0.05
## pruning regulons

3.7 Add Weights

While the pruneRegulon function provides statistics on the joint occurrence of TF-RE-TG, we would like to further estimate the strength of regulation. Biologically, this can be interpreted as the magnitude of gene expression changes induced by transcription factor activity. Epiregulon estimates the regulatory potential using one of the three measures: 1) correlation between TG and TF or between TG and the product of TF and RE, 2) mutual information between TG and TF expression or between TG and the product of TF and RE, or 3) Wilcoxon test statistics of target gene expression in cells jointly expressing all 3 elements vs cells that do not.

Two measures (correlation and Wilcoxon) give both the magnitude and directionality of changes whereas mutational information is always positive. The correlation and mutual information statistics are computed on grouped pseudobulks by user-supplied cluster labels and yield a single weight across all clusters per each TF-RE-target triplet. In contrast, the Wilcoxon method group cells based on the joint expression of TF, RE and TG in each single cell or in cell aggregates. If cell labels are provided, we calculate cluster-specific weights in addition to estimating weights from all the cells. Cell aggregation uses a default value of 10 cells and can help overcome sparsity and speed up computation. If cluster labels are provided, we can obtain weights of individual clusters and all cells combined.

In this example, we apply Wilcoxon test on cell aggregates of 10 cells. We use the Wilcoxon weight method because we are interested in computing cell type-specific weights.

set.seed(1010)
regulon.w <- addWeights(regulon = pruned.regulon,
                        expMatrix  = GeneExpressionMatrix,
                        exp_assay  = "normalizedCounts",
                        peakMatrix = PeakMatrix,
                        peak_assay = "counts",
                        clusters = GeneExpressionMatrix$Clusters2,
                        aggregateCells = TRUE,
                        method = "wilcox",
                        useDim = "IterativeLSI")
## adding weights using wilcoxon...
## performing pseudobulk using an average of 10 cells
regulon.w
## DataFrame with 343577 rows and 14 columns
##          idxATAC         chr     start       end    idxRNA      target  distance     idxTF          tf     corr
##        <integer> <character> <integer> <integer> <integer> <character> <integer> <integer> <character> <matrix>
## 1            891        chr1   9223922   9224422       107        H6PD     68440      1030        ADNP 0.566311
## 2           1154        chr1  11724524  11725024       135       FBXO6       174      1030        ADNP 0.505591
## 3           1476        chr1  16003338  16003838       181        DDI2     59185      1030        ADNP 0.672807
## 4           2329        chr1  25237599  25238099       295       RUNX3     53313      1030        ADNP 0.865375
## 5           2333        chr1  25241815  25242315       295       RUNX3     49097      1030        ADNP 0.751697
## ...          ...         ...       ...       ...       ...         ...       ...       ...         ...      ...
## 343573    131524       chr19  12895246  12895746      8765        KLF1    102071       473     ZSCAN29 0.734430
## 343574    134071       chr19  41768840  41769340      9199       TGFB1     90291       473     ZSCAN29 0.635403
## 343575    135998       chr19  54711800  54712300      9580      MBOAT7     16066       473     ZSCAN29 0.793423
## 343576    144131       chr22  30838237  30838737     11838       SF3A1     83323       473     ZSCAN29 0.502352
## 343577    144587       chr22  37297198  37297698     11901      CSF2RB      9976       473     ZSCAN29 0.672100
##                             pval                     stats                qval                             weight
##                         <matrix>                  <matrix>            <matrix>                           <matrix>
## 1      2.42572e-53:1:1.00000:... 236.3764:0:0.00000000:... 3.77430e-47:1:1:...   0.352108:-0.0572865:0.218160:...
## 2      1.70871e-07:1:0.96773:...  27.3374:0:0.00163667:... 2.42454e-01:1:1:...   0.163694: 0.1313720:0.236942:...
## 3      2.50344e-04:1:1.00000:...  13.4096:0:0.00000000:... 1.00000e+00:1:1:...   0.177100: 0.0000000:0.000000:...
## 4      7.39278e-14:1:1.00000:...  55.9609:0:0.00000000:... 1.08689e-07:1:1:...   0.427978: 0.0000000:0.281650:...
## 5      2.20925e-10:1:1.00000:...  40.2722:0:0.00000000:... 3.19614e-04:1:1:...   0.313465: 0.0000000:0.229459:...
## ...                          ...                       ...                 ...                                ...
## 343573       5.96086e-04:1:1:...         11.788160:0:0:...           1:1:1:... 0.174968:-0.0612248: 0.0000000:...
## 343574       7.23903e-01:1:1:...          0.124784:0:0:...           1:1:1:... 0.157035: 0.1168639:-0.1527121:...
## 343575       4.26510e-02:1:1:...          4.109182:0:0:...           1:1:1:... 0.227676:-0.0954814: 0.0000000:...
## 343576       2.76179e-05:1:1:...         17.575117:0:0:...           1:1:1:... 0.255186:-0.0791276:-0.0727201:...
## 343577       1.12773e-03:1:1:...         10.605155:0:0:...           1:1:1:... 0.253897:-0.1591292: 0.0000000:...

3.8 Calculate TF activity

Finally, the activities for a specific TF in each cell are computed by averaging the weighted expressions of target genes linked to the TF weighted. \[y=\frac{1}{n}\sum_{i=1}^{n} x_i * weight_i\] where \(y\) is the activity of a TF for a cell \(n\) is the total number of targets for a TF \(x_i\) is the log count expression of target i where i in {1,2,…,n} \(weight_i\) is the weight of TF and target i

score.combine <- calculateActivity(expMatrix = GeneExpressionMatrix, 
                                   regulon = regulon.w, 
                                   mode = "weight", 
                                   method = "weightedMean", 
                                   exp_assay = "normalizedCounts")
## calculating TF activity from regulon using weightedmean
## Warning in calculateActivity(expMatrix = GeneExpressionMatrix, regulon = regulon.w, : The weight column contains multiple
## subcolumns but no cluster information was provided. Using first column to compute activity...
## aggregating regulons...
## creating weight matrix...
## calculating activity scores...
## normalize by the number of targets...
head(score.combine[1:5,1:5])
## 5 x 5 sparse Matrix of class "dgCMatrix"
##      scATAC_BMMC_R1#TTATGTCAGTGATTAG-1 scATAC_BMMC_R1#AAGATAGTCACCGCGA-1 scATAC_BMMC_R1#GCATTGAAGATTCCGT-1
## ADNP                         0.1441244                         0.2181751                         0.1829715
## AFF1                         0.1812722                         0.1908101                         0.5706316
## AFF4                         0.1226549                         0.3673765                         0.1364362
## AGO1                         0.1818927                         0.2179344                         0.1032600
## AGO2                         0.0791438                         0.4689620                         0.1131031
##      scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1 scATAC_BMMC_R1#AGTTACGAGAACGTCG-1
## ADNP                         0.1636268                        0.15758333
## AFF1                         0.1928402                        0.19996029
## AFF4                         0.2080821                        0.13736470
## AGO1                         0.1678979                        0.22520235
## AGO2                         0.2267187                        0.06771202

3.9 Differential TF activity test

We can next determine which TFs exhibit differential activities across cell clusters/groups via the findDifferentialActivity function. This function depends on findMarkers function from scran package.

library(epiregulon.extra)
markers  <- findDifferentialActivity(activity_matrix = score.combine, 
                                    clusters = GeneExpressionMatrix$Clusters2, 
                                    pval.type = "some", 
                                    direction = "up", 
                                    test.type = "t")

getSigGenes compiles the different test results into a single dataframe and enables user to supply their desired cutoffs for significance and variable to order by.

markers.sig <- getSigGenes(markers, topgenes = 3 )
## Using a logFC cutoff of 0.2 for class B for direction equal to any
## Using a logFC cutoff of 0.5 for class CD4.M for direction equal to any
## Using a logFC cutoff of 0.4 for class CD4.N for direction equal to any
## Using a logFC cutoff of 0.3 for class CLP for direction equal to any
## Using a logFC cutoff of 0.4 for class Erythroid for direction equal to any
## Using a logFC cutoff of 0.2 for class GMP for direction equal to any
## Using a logFC cutoff of 0.9 for class Mono for direction equal to any
## Using a logFC cutoff of 0.5 for class NK for direction equal to any
## Using a logFC cutoff of 0.2 for class pDC for direction equal to any
## Using a logFC cutoff of 0.3 for class PreB for direction equal to any
## Using a logFC cutoff of 0.2 for class Progenitor for direction equal to any

3.10 Visualizing TF activities

Epiregulon also provides multiple options for visualizing the inferred TF activities by reduced dimensional space

tSNE or UMAP plots:

options(ggrastr.default.dpi=300)
tfs_interest <- c("EBF1","PAX5", "GATA3","SPI1")
plotActivityDim(sce = GeneExpressionMatrix,
                activity_matrix = score.combine[tfs_interest,], 
                tf = tfs_interest, 
                dimtype = "UMAP",
                nrow=2,
                ncol=2,
                point_size=0.1,
                rasterise = TRUE)

We can compare the activity with gene expression of the same TFs.

plotActivityDim(sce = GeneExpressionMatrix, 
                activity_matrix = assay(GeneExpressionMatrix, "normalizedCounts")[tfs_interest,], 
                tf = tfs_interest, 
                dimtype = "UMAP",
                nrow=2,
                ncol=2,
                legend.label = "Gex",
                colors = c("grey","blue"),
                point_size=0.1,
                rasterise = TRUE)

We can also plot violin plot to visualize TF activity.

plotActivityViolin(activity_matrix = score.combine, 
                   tf = tfs_interest, 
                   clusters = GeneExpressionMatrix$Clusters2,
                   legend.label = "Gex",
                   nrow=2,
                   ncol=2)

We plot violin plot to visualize TF gene expression.

plotActivityViolin(activity_matrix = assay(GeneExpressionMatrix, "normalizedCounts")[tfs_interest,],  
                   tf = tfs_interest, 
                   clusters = GeneExpressionMatrix$Clusters2,
                   nrow=2,
                   ncol=2,
                   legend.label = "gene expression")

We can visualize the different TFs in a bubble plot:

plotBubble(activity_matrix = score.combine, 
           tf = tfs_interest, 
           GeneExpressionMatrix$Clusters2, 
           bubblesize = "FDR")

We visualize the top differential TFs based on activity.

plotBubble(activity_matrix = score.combine, 
           tf = markers.sig$tf, 
           GeneExpressionMatrix$Clusters2, 
           bubblesize = "FDR")

3.11 Geneset enrichment

Sometimes we are interested to know what pathways are enriched in the regulon of a particular TF. We can perform geneset enrichment using the enricher function from clusterProfiler.

Here we first download Hallmark and C2 signatures from hallmark and then perform gene set enrichment of the known lineage factors. As expected, EBF1 is consistent with a B cell lineage factor, GATA3 and RUNX3 with lymphoid lineage and SPI1 with myeloid lineage.

#retrieve genesets
H <- EnrichmentBrowser::getGenesets(org = "hsa", 
                                    db = "msigdb", 
                                    cat = "H", 
                                    gene.id.type = "SYMBOL" )
## Using cached version from 2024-03-19 13:20:14
C2 <- EnrichmentBrowser::getGenesets(org = "hsa", 
                                     db = "msigdb", 
                                     cat = "C2",
                                     gene.id.type = "SYMBOL" )
## Using cached version from 2024-03-19 11:49:25
#combine genesets and convert genesets to be compatible with enricher
gs <- c(H, C2)
gs.list <- do.call(rbind,lapply(names(gs), function(x) 
  {data.frame(gs=x, genes=gs[[x]])}))

enrichresults <- regulonEnrich(TF = tfs_interest, 
                               regulon = regulon.w, 
                               weight = "weight",
                               weight_cutoff = 0, 
                               genesets = gs.list)
## EBF1
## PAX5
## GATA3
## SPI1
#plot results
enrichPlot(results = enrichresults, ncol=2)

3.12 Differential Network analysis

In addition to looking at the summed TF activity, a second approach to investigate differential TF activity is to compare and contrast target genes or network topology. In this example, we know that EBF1 is a B cell lineage factor. If we plot the differential network of EBF1 using the regulon with cluster-specific weights, we can see that EBF1 has many more targets in PreB cells than it has in CD4 memory T cells.

plotDiffNetwork(regulon.w,
                cutoff = 0,
                tf = c("EBF1"),
                weight = "weight",
                clusters = c("PreB","CD4.M"),
                layout = "stress")
## Replacement of na values for weights with 0
## Building graph using weight as edge weights

Sometimes, we are interested to identify interaction partners of the TFs of interest. This can be achieved by comparing the overlap of the targets genes for all the TFs and identify the most similar TFs by Jaccard similarity. To illustrate this function, we take a look at the top most similar 20 TFs to EBF1, and we successfully identify PAX5 as the most similar TF. Both PAX5 and EBF1 are important factors for B cell development (https://www.nature.com/articles/ni.2641).

library(ggplot2)
# construct a graph of the preB cells
preB_network <- buildGraph(regulon.w, weights = "weight", cluster="PreB")
## Building graph using weight as edge weights
# compute a similarity matrix of all TFs
similarity_score <- calculateJaccardSimilarity(preB_network)

# Focus on EBF1
similarity_score_EBF1 <- similarity_score[, "EBF1"]
similarity_df <- data.frame(similarity = head(sort(similarity_score_EBF1, 
                                                   decreasing = TRUE),20),
                            TF = names(head(sort(similarity_score_EBF1,
                                                 decreasing = TRUE),20)))

similarity_df$TF <- factor(similarity_df$TF, levels = rev(unique(similarity_df$TF)))

# plot top TFs most similar to EBF1
topTFplot <- ggplot(similarity_df, aes(x=TF, y=similarity)) +
  geom_bar(stat="identity") +
  coord_flip() +
  ggtitle("EBF1 similarity") +
  theme_classic()

print(topTFplot)

In order to convince ourselves that our differential network is statistically significant, we permute the edges and obtain a background graph from averaging many iterations. Here, we plot the differential network graph subtracted by permuted graphs.

# create a permuted graph by rewiring the edges 100 times
permute_matrix <- permuteGraph(preB_network, "EBF1", 100, p=1)
permute_matrix <- permute_matrix[names(similarity_score_EBF1),]
diff_matrix <- similarity_score_EBF1-rowMeans(permute_matrix)

diff_matrix_df <- data.frame(similarity = head(sort(diff_matrix, 
                                                    decreasing = TRUE),20),
                            TF = names(head(sort(diff_matrix,
                                                 decreasing = TRUE),20)))

diff_matrix_df$TF <- factor(diff_matrix_df$TF, levels = rev(unique(diff_matrix_df$TF)))

# plot top TFs most similar to EBF1
topTFplot <- ggplot(diff_matrix_df, aes(x=TF, y=similarity)) +
            geom_bar(stat="identity") +
            coord_flip() +
            ggtitle("background subtracted EBF1 similarity ") +
            theme_classic()
print(topTFplot)

# obtain empirical p-values
p_matrix <- rowMeans(apply(permute_matrix, 2, function(x) {x > similarity_score_EBF1}))
p_matrix[names(head(sort(diff_matrix,decreasing = TRUE),20))]
##   PAX5   IRF4   TCF3  TCF12   RAG2  NIPBL POU2F2   TCF4   RAG1    RB1    MYB TRIM22   EZH2  EP300  MEF2A   CDK9    ERG 
##      0      0      0      0      0      0      0      0      0      0      0      0      0      0      0      0      0 
##  FOXP1 CREBBP BCL11A 
##      0      0      0

Next, we are interested to compare the networks of two cell types, in this case, CD4 memory T cells (CD4.M) vs Monocytes (mono) cells. We build an edge subtracted graph and then calculate the degree centrality of the subtracted graph. We normalize centrality using the default square root function. The top 5 most positive TFs represent lineage factors more active in NK cells whereas the bottom 5 TFs present lineage factors enriched in CD4. We successfully identified the myeloid factor SPI1 to be associated with monocytes and Th1 factor TBX21 to be associated with CD4 T cells.

#regulon.w.2 <- regulon.w
#regulon.w <- readRDS("/gstore/project/lineage/manuscript/epiregulon/OUTPUT/regulon.w.rds")
# construct a graph of the CD4.M and NK cells respectively
CD4.M_network <- buildGraph(regulon.w, weights = "weight", cluster="CD4.M")
## Building graph using weight as edge weights
Mono_network <- buildGraph(regulon.w, weights = "weight", cluster="Mono")
## Building graph using weight as edge weights
# construct a difference graph
diff_graph <- buildDiffGraph(Mono_network,CD4.M_network, abs_diff = FALSE)
diff_graph <- addCentrality(diff_graph)
diff_graph <- normalizeCentrality(diff_graph)
rank_table <- rankTfs(diff_graph)

library(ggplot2)
ggplot(rank_table, aes(x = rank, y = centrality)) +
    geom_point() +
    ggrepel::geom_text_repel(data = rbind(head(rank_table, 10), 
                                          tail(rank_table, 10)), 
                             aes(label = tf), 
                             nudge_x = 0, nudge_y = 0, box.padding = 0.5, max.overlaps = Inf) +
    theme_classic() + ggtitle ("differential TFs (Mono-CD4.M) ranked by degree centrality")

We can further explore interacting factors with the myeloid factor SPI1 using the same Jaccard similarity approach. We found CEBPA as the most similar TF as SPI1. SPI1 and CEBPA are known to be important for differentiation into myeloid cells (https://www.cell.com/cell-reports/pdfExtended/S2211-1247(18)30745-9).

library(igraph)
diff_graph_filter <- subgraph.edges(diff_graph, 
                                    E(diff_graph)[E(diff_graph)$weight>0], 
                                    del=TRUE)


# compute a similarity matrix of all TFs
similarity_score <- calculateJaccardSimilarity(diff_graph_filter)

# Focus on SPI1
similarity_score_SPI1 <- similarity_score[, "SPI1"]
similarity_df <- data.frame(similarity = head(sort(similarity_score_SPI1, 
                                                   decreasing = TRUE),20),
                            TF = names(head(sort(similarity_score_SPI1,
                                                 decreasing = TRUE),20)))

similarity_df$TF <- factor(similarity_df$TF, 
                           levels = rev(unique(similarity_df$TF)))

# plot top TFs most similar to SPI1
topTFplot <- ggplot(similarity_df, aes(x=TF, y=similarity)) +
  geom_bar(stat="identity") +
  coord_flip() +
  ggtitle("SPI1 similarity") +
  theme_classic()

print(topTFplot)

3.13 Session Info

sessionInfo()
## R Under development (unstable) (2023-12-04 r85659)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 18.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so 
## LAPACK: /usr/local/lib/R/lib/libRlapack.so;  LAPACK version 3.11.0
## 
## Random number generation:
##  RNG:     L'Ecuyer-CMRG 
##  Normal:  Inversion 
##  Sample:  Rejection 
##  
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               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    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
##  [1] parallel  stats4    grid      stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] igraph_2.0.3                      msigdbr_7.5.1                     epiregulon.extra_0.99.5          
##  [4] BSgenome.Hsapiens.UCSC.hg38_1.4.5 BSgenome_1.71.2                   rtracklayer_1.63.1               
##  [7] BiocIO_1.13.0                     Biostrings_2.71.4                 XVector_0.43.1                   
## [10] epiregulon_0.99.5                 scMultiome_1.3.0                  SingleCellExperiment_1.25.0      
## [13] MultiAssayExperiment_1.29.1       ExperimentHub_2.11.1              AnnotationHub_3.11.1             
## [16] BiocFileCache_2.11.1              dbplyr_2.4.0                      BiocStyle_2.31.0                 
## [19] rhdf5_2.47.6                      SummarizedExperiment_1.33.3       Biobase_2.63.0                   
## [22] RcppArmadillo_0.12.8.1.0          Rcpp_1.0.12                       Matrix_1.6-4                     
## [25] GenomicRanges_1.55.3              GenomeInfoDb_1.39.9               IRanges_2.37.1                   
## [28] S4Vectors_0.41.4                  BiocGenerics_0.49.1               sparseMatrixStats_1.15.0         
## [31] MatrixGenerics_1.15.0             matrixStats_1.2.0                 data.table_1.15.2                
## [34] stringr_1.5.1                     plyr_1.8.9                        magrittr_2.0.3                   
## [37] ggplot2_3.5.0                     gtable_0.3.4                      gtools_3.9.5                     
## [40] gridExtra_2.3                     devtools_2.4.5                    usethis_2.2.3                    
## [43] ArchR_1.0.3                      
## 
## loaded via a namespace (and not attached):
##   [1] fs_1.6.3                    bitops_1.0-7                enrichplot_1.23.1           DirichletMultinomial_1.45.0
##   [5] TFBSTools_1.41.0            RColorBrewer_1.1-3          HDO.db_0.99.1               httr_1.4.7                 
##   [9] Rgraphviz_2.47.0            profvis_0.3.8               tools_4.4.0                 backports_1.4.1            
##  [13] utf8_1.2.4                  R6_2.5.1                    HDF5Array_1.31.6            lazyeval_0.2.2             
##  [17] rhdf5filters_1.15.4         urlchecker_1.0.1            withr_3.0.0                 cli_3.6.2                  
##  [21] Cairo_1.6-2                 scatterpie_0.2.1            labeling_0.4.3              sass_0.4.9                 
##  [25] KEGGgraph_1.63.0            readr_2.1.5                 yulab.utils_0.1.4           Rsamtools_2.19.3           
##  [29] gson_0.1.0                  DOSE_3.29.2                 R.utils_2.12.3              scater_1.31.2              
##  [33] sessioninfo_1.2.2           limma_3.59.6                rstudioapi_0.15.0           RSQLite_2.3.5              
##  [37] gridGraphics_0.5-1          generics_0.1.3              dplyr_1.1.4                 GO.db_3.18.0               
##  [41] ggbeeswarm_0.7.2            fansi_1.0.6                 abind_1.4-5                 R.methodsS3_1.8.2          
##  [45] lifecycle_1.0.4             yaml_2.3.8                  edgeR_4.1.18                qvalue_2.35.0              
##  [49] SparseArray_1.3.4           blob_1.2.4                  promises_1.2.1              dqrng_0.3.2                
##  [53] crayon_1.5.2                miniUI_0.1.1.1              lattice_0.22-5              beachmat_2.19.1            
##  [57] cowplot_1.1.3               annotate_1.81.2             KEGGREST_1.43.0             pillar_1.9.0               
##  [61] knitr_1.45                  metapod_1.11.1              fgsea_1.29.0                rjson_0.2.21               
##  [65] codetools_0.2-19            fastmatch_1.1-4             glue_1.7.0                  ggfun_0.1.4                
##  [69] remotes_2.5.0               treeio_1.27.0               vctrs_0.6.5                 png_0.1-8                  
##  [73] poweRlaw_0.80.0             cachem_1.0.8                xfun_0.42                   S4Arrays_1.3.6             
##  [77] mime_0.12                   tidygraph_1.3.1             pracma_2.4.4                statmod_1.5.0              
##  [81] bluster_1.13.0              ellipsis_0.3.2              nlme_3.1-164                ggtree_3.11.1              
##  [85] bit64_4.0.5                 filelock_1.0.3              bslib_0.6.1                 irlba_2.3.5.1              
##  [89] vipor_0.4.7                 colorspace_2.1-0            seqLogo_1.69.0              DBI_1.2.2                  
##  [93] ggrastr_1.0.2               tidyselect_1.2.1            bit_4.0.5                   compiler_4.4.0             
##  [97] curl_5.2.1                  graph_1.81.0                BiocNeighbors_1.21.2        DelayedArray_0.29.9        
## [101] shadowtext_0.1.3            bookdown_0.38               checkmate_2.3.1             scales_1.3.0               
## [105] caTools_1.18.2              rappdirs_0.3.3              digest_0.6.35               motifmatchr_1.25.0         
## [109] rmarkdown_2.26              htmltools_0.5.7             pkgconfig_2.0.3             highr_0.10                 
## [113] fastmap_1.1.1               rlang_1.1.3                 htmlwidgets_1.6.4           shiny_1.8.0                
## [117] DelayedMatrixStats_1.25.1   farver_2.1.1                jquerylib_0.1.4             jsonlite_1.8.8             
## [121] BiocParallel_1.37.1         GOSemSim_2.29.1             R.oo_1.26.0                 BiocSingular_1.19.0        
## [125] RCurl_1.98-1.14             ggplotify_0.1.2             scuttle_1.13.1              GenomeInfoDbData_1.2.11    
## [129] patchwork_1.2.0             Rhdf5lib_1.25.1             munsell_0.5.0               ape_5.7-1                  
## [133] babelgene_22.9              viridis_0.6.5               EnrichmentBrowser_2.33.1    stringi_1.8.3              
## [137] ggraph_2.2.1                MASS_7.3-60.1               zlibbioc_1.49.3             pkgbuild_1.4.4             
## [141] ggrepel_0.9.5               CNEr_1.39.0                 graphlayouts_1.1.1          splines_4.4.0              
## [145] hms_1.1.3                   locfit_1.5-9.9              reshape2_1.4.4              ScaledMatrix_1.11.1        
## [149] pkgload_1.3.4               TFMPvalue_0.0.9             BiocVersion_3.19.1          XML_3.99-0.16.1            
## [153] evaluate_0.23               scran_1.31.3                BiocManager_1.30.22         tweenr_2.0.3               
## [157] tzdb_0.4.0                  httpuv_1.6.14               polyclip_1.10-6             tidyr_1.3.1                
## [161] purrr_1.0.2                 ggforce_0.4.2               rsvd_1.0.5                  xtable_1.8-4               
## [165] restfulr_0.0.15             tidytree_0.4.6              later_1.3.2                 viridisLite_0.4.2          
## [169] tibble_3.2.1                aplot_0.2.2                 clusterProfiler_4.11.0      memoise_2.0.1              
## [173] beeswarm_0.4.0              AnnotationDbi_1.65.2        GenomicAlignments_1.39.4    cluster_2.1.6              
## [177] GSEABase_1.65.1