CSOA 0.99.2
This tutorial showcases some advanced functions of CSOA.
In addition to CSOA, you need to install patchwork, scRNAseq, scuttle and stringr for this tutorial.
First, we will repeat the setup from the CSOA tutorial. We need the
BaronPancreasData
dataset from scRNAseq
, and
we load the PanglaoDB-generated
list of acinar markers.
library(CSOA)
library(ggplot2)
library(patchwork)
library(scRNAseq)
library(scuttle)
library(stringr)
library(Seurat)
sceObj <- BaronPancreasData('human')
sceObj <- logNormCounts(sceObj)
seuratObj <- as.Seurat(sceObj)
acinarMarkers <- c('PRSS1', 'KLK1', 'CTRC', 'PNLIP', 'AKR1C3', 'CTRB1',
'DUOXA2', 'ALDOB', 'REG3A', 'SERPINA3', 'PRSS3', 'REG1B',
'CFB', 'GDF15', 'MUC1','ANPEP', 'ANGPTL4', 'OLFM4',
'GSTA1', 'LGALS2', 'PDZK1IP1', 'RARRES2', 'CXCL17',
'UBD', 'GSTA2', 'LYZ', 'RBPJL', 'PTF1A', 'CELA3A',
'SPINK1', 'ZG16', 'CEL', 'CELA2A', 'CPB1', 'CELA1',
'PNLIPRP1', 'RNASE1', 'AMY2B', 'CPA2','CPA1', 'CELA3B',
'CTRB2', 'PLA2G1B', 'PRSS2', 'CLPS', 'REG1A', 'SYCN')
A distinctive feature of CSOA is that the calculation of per-cell scores does not necessarily use all the genes in the input gene signature. If a gene did not register any top overlap, it will not feature in the calculation of CSOA scores.
We now run CSOA with the option of calculating the contribution of individual gene pairs to the CSOA score toggled on using pairFileName
:
seuratObj <- runCSOA(seuratObj, list(CSOA_acinar = acinarMarkers),
pairFileTemplate='pairs')
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 194 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Saving pair scores file: pairsCSOA_acinar.qs...
#> Computing per-cell gene signature scores...
Next, we inspect the resulting data frame using qGrab
, a wrapper around
qread
from the qs
package which deletes the file after reading it:
acinarPairs <- qGrab('pairsCSOA_acinar.qs')
head(acinarPairs)
#> gene1 gene2 overlapScore overlapRank pairScore pairRank revCumsum
#> 507 CPA1 PRSS2 0.8279889 22 1.643546 1 100.00000
#> 662 CTRC PRSS2 0.8279889 22 1.403776 2 98.35645
#> 815 KLK1 PRSS2 0.8886735 12 1.379073 3 96.95268
#> 327 CELA2A PRSS2 0.7633825 34 1.332878 4 95.57361
#> 977 PRSS1 PRSS2 0.5808436 82 1.309812 5 94.24073
#> 572 CPB1 PRSS2 0.7633825 34 1.287813 6 92.93092
Using the overlapGenes
function, we find that only 27
out of the 47
input genes contributed directly to the CSOA score:
genes <- overlapGenes(acinarPairs)
nGenes <- length(genes)
nGenes
#> [1] 27
length(acinarMarkers)
#> [1] 47
setdiff(acinarMarkers, genes)
#> [1] "AKR1C3" "DUOXA2" "SERPINA3" "GDF15" "MUC1" "ANPEP"
#> [7] "ANGPTL4" "OLFM4" "LGALS2" "PDZK1IP1" "CXCL17" "UBD"
#> [13] "LYZ" "RBPJL" "PTF1A" "ZG16" "CELA1" "RNASE1"
#> [19] "AMY2B" "CPA2"
Next, we run CSOA with only the 27
genes as input:
seuratObj <- runCSOA(seuratObj, list(CSOA_acinar2 = genes))
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 86 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Computing per-cell gene signature scores...
We aim to compare the results with those obtained using the original 47
acinar markers. To this end, we build a function that computes and prints several statistics of interest here—Pearson correlation, and mean, median, maximum and minimum of the absolute-value differences between the two sets of CSOA scores:
printStats <- function(seuratObj, col1, col2){
paste0('Pearson correlation: ', cor(seuratObj[[]][[col1]],
seuratObj[[]][[col2]]))
absDiffs <- abs(seuratObj[[]][[col1]] - seuratObj[[]][[col2]])
absDiffsStats <- sapply(list(mean, median, max, min),
function(fun) fun(absDiffs))
names(absDiffsStats) <- paste0(c('mean', 'median', 'max', 'min'),
'AbsDiff')
absDiffsStats
}
The results are quite close to those obtained using all the 47
genes:
printStats(seuratObj, 'CSOA_acinar', 'CSOA_acinar2')
#> meanAbsDiff medianAbsDiff maxAbsDiff minAbsDiff
#> 0.0047680035 0.0001103177 0.1434652424 0.0000000000
It is instructive to think why the results between the two runs were not
identical. This is because the additional overlaps computed for the 47
acinar
markers impacted each individual component of the overlap rank (p-value rank,
ratio rank), inducing changes in the overlap ranking and subsequent selection
and scoring.
We can further refine gene signatures by eliminating overlaps which account only for a small percent of the total score, and subsequently setting the corresponding genes as new input genes.
We return to the acinarPairs
data frame. The revCumsum
column shows
that 90%
of the total CSOA score is accounted for by the top-scoring
128
gene pairs:
acinarPairsSub <- subset(acinarPairs, revCumsum > 10)
nrow(acinarPairsSub)
#> [1] 128
The overlapGenes
function finds the 24
genes that correspond to these pairs:
genes90 <- overlapGenes(acinarPairsSub)
genes90
#> [1] "CPA1" "CTRC" "KLK1" "CELA2A" "PRSS1" "CPB1"
#> [7] "PNLIP" "CELA3B" "PRSS2" "CELA3A" "PNLIPRP1" "CTRB1"
#> [13] "CTRB2" "REG1B" "PLA2G1B" "PRSS3" "GSTA2" "GSTA1"
#> [19] "CLPS" "CEL" "SYCN" "REG1A" "REG3A" "RARRES2"
nGenes90 <- length(genes90)
nGenes90
#> [1] 24
Next, we run CSOA using solely these 24
genes as input genes. The results remain close to those obtained using all acinar markers:
seuratObj <- runCSOA(seuratObj, list(CSOA_acinar3 = genes90))
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 97 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Computing per-cell gene signature scores...
printStats(seuratObj, 'CSOA_acinar', 'CSOA_acinar3')
#> meanAbsDiff medianAbsDiff maxAbsDiff minAbsDiff
#> 0.0068344918 0.0001244179 0.1725938747 0.0000000000
This section will demonstrate CSOA’s ability to extract the most biologically meaningful genes from an input gene set. First, as random numbers are going to be involved, we set a seed to ensure reproducibility:
set.seed(123)
We will proceed by replacing randomly chosen genes among the top 24
acinar
markers with random genes from the Seurat object, and running CSOA using the
resulting gene set.
First, we build a function for this task:
runCSOAReplace <- function(seuratObj, markers, nReplacedGenes,
pairFileTemplate = 'pairs'){
genesComplement <- setdiff(rownames(seuratObj), markers)
geneSet <- list(c(sample(markers, length(markers) - nReplacedGenes),
sample(genesComplement, nReplacedGenes)))
names(geneSet) <- paste0('CSOA_replace', nReplacedGenes)
seuratObj <- runCSOA(seuratObj, geneSet, pairFileTemplate=pairFileTemplate)
return(seuratObj)
}
Now we run CSOA on a set in which half of top 24
acinar markers (randomly chosen) have been replaced by random genes. The results are very close to those obtained using the original 24
markers, though with an increase in the maximum absolute difference—CSOA no longer recognizes a very few acinar cells as such, because of the lower number of input acinar markers.
nReplacedGenes <- ceiling(nGenes90 / 2)
seuratObj <- runCSOAReplace(seuratObj, genes90, nReplacedGenes)
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 33 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Saving pair scores file: pairsCSOA_replace12.qs...
#> Computing per-cell gene signature scores...
newCol <- paste0('CSOA_replace', nReplacedGenes)
printStats(seuratObj, 'CSOA_acinar3', newCol)
#> meanAbsDiff medianAbsDiff maxAbsDiff minAbsDiff
#> 0.0113040121 0.0002535866 0.2785675871 0.0000000000
VlnPlot(seuratObj, newCol, group.by='label') + NoLegend()
The noise introduced by random genes did not directly affect CSOA results at all. Using the overlapGenes
function, we can verify that none of the random genes participated in the 23
top overlaps:
length(setdiff(overlapGenes(qGrab('pairsCSOA_replace12.qs')), genes90))
#> [1] 0
We now replace all but 2
of the top 24
acinar markers with random genes and run CSOA again. The mean, median and minimum absolute differences remain very small, and the correlation with the original results remains very high. However, the maximum absolute difference has become substantial, indicating that an additional small number of cells could no longer be detected as acinar cells. Noise now makes an effect, though a small one: a single random gene (PTGR1
) introduces two overlaps into CSOA scoring, which together account for 27.8%
of the total CSOA score.
nReplacedGenes <- nGenes90 - 2
seuratObj <- runCSOAReplace(seuratObj, genes90, nReplacedGenes)
#> Computing percentile sets...
#> Warning in percentileSets(geneSetExp, percentile): 4 gene(s) had no top cells
#> at the indicated percentile. These are now excluded from the gene signature.
#> Assessing gene overlaps...
#> 3 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Saving pair scores file: pairsCSOA_replace22.qs...
#> Computing per-cell gene signature scores...
newCol <- paste0('CSOA_replace', nReplacedGenes)
printStats(seuratObj, 'CSOA_acinar3', newCol)
#> meanAbsDiff medianAbsDiff maxAbsDiff minAbsDiff
#> 0.01975495 0.00000000 0.54736806 0.00000000
dfReplace <- qGrab('pairsCSOA_replace22.qs')
dfReplace
#> gene1 gene2 overlapScore overlapRank pairScore pairRank revCumsum
#> 70 CELA2A CTRB1 1.0000000 1 72.20135 1 100.00000
#> 152 CTRB1 PTGR1 0.6201145 2 16.47713 2 27.79865
#> 82 CELA2A PTGR1 0.6201145 2 11.32151 3 11.32151
setdiff(overlapGenes(dfReplace), genes90)
#> [1] "PTGR1"
VlnPlot(seuratObj, newCol, group.by='label') + NoLegend()
These results have implications upon the construction of input gene sets for CSOA. Due to CSOA’s efficient noise-filtering ability, it is generally better to err on the side of including more genes in the input, even though this will incur higher computational costs.
Cell signatures do not necessarily characterize a single cell type or state. Therefore, it is desirable that a gene set analysis method is able to recognize the different cell types or states characterized by different genes in the input set.
To illustrate this functionality of CSOA, we will first construct a mixed signature using acinar and stellate cell markers selected from PanglaoDB:
stellateMarkers <- c('COL6A1', 'RGS5', 'COL1A1', 'MMP11', 'THY1', 'COL6A3',
'SFRP2', 'COL1A2', 'TNFAIP6', 'TIMP3', 'SPARC', 'COL3A1',
'MGP', 'COL6A2', 'COL4A1', 'FN1', 'SPON2', 'TIMP1',
'TGFB1', 'INHBA', 'PDGFRA', 'NDUFA4L2', 'MMP14', 'CTGF',
'CYGB', 'KRT10', 'PDGFRB', 'DYNLT1', 'GEM')
mixedMarkers <- union(acinarMarkers, stellateMarkers)
Now we run CSOA with the mixed markers as input. As expected, we see enrichment of CSOA scores among both acinar and stellate cells:
seuratObj <- runCSOA(seuratObj, list(CSOA_mixed = mixedMarkers),
pairFileTemplate='pairs')
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 249 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Saving pair scores file: pairsCSOA_mixed.qs...
#> Computing per-cell gene signature scores...
VlnPlot(seuratObj, 'CSOA_mixed', group.by='label') + NoLegend()
We can interpret the overlap data frame as a graph, with genes as vertices and overlaps as edges. We can build and visualize the graph as follows:
dfNetwork <- qGrab('pairsCSOA_mixed.qs')
networkPlot(dfNetwork, rankCol='overlapRank')
We remark that the graph is not connected: it has no vertex from which we can reach all other vertices by travelling on the graph edges. Instead, some subgraphs are connected. We refer to these as connected components. Their significance is that they can be interpreted as gene modules—groups of genes linked by the highly significant overlaps of some of their corresponding cell sets. The graph contains two connected components, 1
and 2
:
dfNetwork <- connectedComponents(dfNetwork)
dplyr::count(dfNetwork, component)
#> component n
#> 1 1 133
#> 2 2 116
Next, we are interested in scoring the major gene modules in order to ascertain the identity of the cells they characterize. We can score the major components separately as follows:
components <- c(1, 2)
seuratObj <- scoreModules(seuratObj, dfNetwork, components)
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 101 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Computing per-cell gene signature scores...
#> 87 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Computing per-cell gene signature scores...
p1 <- VlnPlot(seuratObj, 'CSOA_component1', group.by='label') + NoLegend() +
theme(axis.text.y = element_blank())
p2 <- VlnPlot(seuratObj, 'CSOA_component2', group.by='label') + NoLegend() +
theme(axis.text.y = element_blank())
p1 / p2
Thus, component 1
is an acinar one and component 2
is a stellate one. The genes defining each component can be extracted from the overlap matrix after connectedComponents
has been run:
genes1 <- overlapGenes(subset(dfNetwork, component == 1))
genes1
#> [1] "CPA1" "PNLIP" "CTRC" "CELA2A" "CPB1" "KLK1"
#> [7] "PRSS2" "CELA3B" "CELA3A" "PRSS1" "CTRB1" "CTRB2"
#> [13] "GSTA2" "PLA2G1B" "CLPS" "GSTA1" "PRSS3" "REG1B"
#> [19] "CEL" "RARRES2" "SYCN" "REG1A" "PNLIPRP1" "REG3A"
#> [25] "SPINK1"
genes2 <- overlapGenes(subset(dfNetwork, component == 2))
genes2
#> [1] "COL6A2" "COL6A1" "COL1A1" "COL3A1" "COL1A2" "MMP14" "COL6A3" "FN1"
#> [9] "SPARC" "INHBA" "CYGB" "PDGFRB" "SFRP2" "MMP11" "SPON2" "MGP"
#> [17] "TIMP3" "THY1" "TIMP1" "PDGFRA"
It may happen, though, that the network of overlaps generated by CSOA for a mixed signature is connected. Let us construct a mixed signature of acinar and ductal markers, run CSOA, and identity the connected components of the graph determined by the top overlaps:
ductalMarkers <- c('CFTR', 'SERPINA5', 'SLPI', 'TFF1', 'CFB', 'LGALS4',
'CTSH', 'PERP', 'PDLIM3', 'WFDC2', 'SLC3A1', 'AQP1',
'ALDH1A3', 'VTCN1', 'KRT19', 'TFF2', 'KRT7', 'CLDN4',
'LAMB3', 'TACSTD2', 'CCL2', 'DCDC2','CXCL2', 'CLDN10',
'HNF1B', 'KRT20', 'MUC1', 'ONECUT1', 'AMBP', 'HHEX',
'ANXA4', 'SPP1', 'PDX1', 'SERPINA3', 'GDF15', 'AKR1C3',
'MMP7', 'DEFB1', 'SERPING1', 'TSPAN8', 'CLDN1', 'S100A10',
'PIGR')
mixedMarkers <- union(acinarMarkers, ductalMarkers)
seuratObj <- runCSOA(seuratObj, list(CSOA_mixed = mixedMarkers),
pairFileTemplate='pairs')
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 760 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Saving pair scores file: pairsCSOA_mixed.qs...
#> Computing per-cell gene signature scores...
VlnPlot(seuratObj, 'CSOA_mixed', group.by='label') + NoLegend()
dfNetwork <- qGrab('pairsCSOA_mixed.qs')
dfNetwork <- connectedComponents(dfNetwork)
dplyr::count(dfNetwork, component)
#> component n
#> 1 1 760
The violin plot shows enrichment in both the acinar and ductal groups,
but the overlap graph is connected. To divide the mixed signature into acinar
and ductal components, we will run CSOA with a non-null value set for
the jaccardCutoff
parameter:
seuratObj <- runCSOA(seuratObj, list(CSOA_mixed = mixedMarkers),
jaccardCutoff=1/3, pairFileTemplate='pairs')
#> Computing percentile sets...
#> Assessing gene overlaps...
#> 760 overlaps have been selected for Jaccard-based filtering.
#> 221 edges with low neighbor Jaccard scores have been removed.
#> 42 edges with low neighbor Jaccard scores have been removed.
#> 3 edges with low neighbor Jaccard scores have been removed.
#> 3 edges with low neighbor Jaccard scores have been removed.
#> 5 edges with low neighbor Jaccard scores have been removed.
#> 486 overlaps will be used in the calculation of CSOA scores.
#> Normalizing expression matrix by rows...
#> Computing per-cell scores for gene pairs...
#> Saving pair scores file: pairsCSOA_mixed.qs...
#> Computing per-cell gene signature scores...
This function has reduced the number of overlaps from 760 to 486 by selectively removing overlaps for which the neighbors of the corresponding genes overlapped little, as calculated using the Jaccard score. The resulting graph has two connected components:
dfNetwork <- qGrab('pairsCSOA_mixed.qs')
networkPlot(dfNetwork, rankCol='overlapRank')
These correspond to acinar and ductal cells, respectively:
dfNetwork <- connectedComponents(dfNetwork)
components <- unique(dfNetwork$component)
seuratObj <- scoreModules(seuratObj, dfNetwork, components)
p1 <- VlnPlot(seuratObj, 'CSOA_component1', group.by='label') + NoLegend() +
theme(axis.text.y=element_blank())
p2 <- VlnPlot(seuratObj, 'CSOA_component2', group.by='label') + NoLegend() +
theme(axis.text.y=element_blank())
p1 / p2
To display gene participation in top overlaps, CSOA provides the geneRadialPlot
function:
geneRadialPlot(acinarPairs,
title='Top 100 overlap genes - Acinar markers')
#> Finding frequencies of gene degrees...
#> Finding gene coordinates...
Additionally, we can visualize and compare the genes involved in the top overlaps across multiple gene sets. We will reload the alpha and gamma markers from the Getting started with CSOA tutorial, and run again CSOA with four gene sets. To facilitate visualization, we will select the top 15
overlaps for each gene set:
alphaMarkers <- c('GCG', 'TTR', 'PCSK2', 'FXYD5', 'LDB2', 'MAFB',
'CHGA', 'SCGB2A1', 'GLS', 'FAP', 'DPP4', 'GPR119',
'PAX6', 'NEUROD1', 'LOXL4', 'PLCE1', 'GC', 'KLHL41',
'FEV', 'PTGER3', 'RFX6', 'SMARCA1', 'PGR', 'IRX1',
'UCP2', 'RGS4', 'KCNK16', 'GLP1R', 'ARX', 'POU3F4',
'RESP18', 'PYY', 'SLC38A5', 'TM4SF4', 'CRYBA2', 'SH3GL2',
'PCSK1', 'PRRG2', 'IRX2', 'ALDH1A1','PEMT', 'SMIM24',
'F10', 'SCGN', 'SLC30A8')
gammaMarkers <- c('PPY', 'ABCC9', 'FGB', 'ZNF503', 'MEIS1', 'LMO3', 'EGR3',
'CHN2', 'PTGFR', 'ENTPD2', 'AQP3', 'THSD7A', 'CARTPT',
'ISL1', 'PAX6', 'NEUROD1', 'APOBEC2', 'SEMA3E', 'SLITRK6',
'SERTM1', 'PXK', 'PPY2P', 'ETV1', 'ARX', 'CMTM8', 'SCGB2A1',
'FXYD2', 'SCGN')
geneSets <- list(acinarMarkers, alphaMarkers, ductalMarkers, gammaMarkers)
names(geneSets) <- c('CSOA_acinar', 'CSOA_alpha', 'CSOA_ductal', 'CSOA_gamma')
seuratObj <- runCSOA(seuratObj, geneSets, pairFileTemplate='pairs',
keepOverlapOrder=TRUE)
#> Warning in percentileSets(geneSetExp, percentile): 1 gene(s) had no top cells
#> at the indicated percentile. These are now excluded from the gene signature.
geneRadialPlot(lapply(paste0('pairs', names(geneSets), '.qs'), qGrab),
groupLegendName='Gene set',
groupNames=str_remove(names(geneSets), 'CSOA_'),
cutoff=15,
title='Top 15 overlap genes',
extraCircles=2)
sessionInfo()
#> R version 4.5.1 Patched (2025-08-23 r88802)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.3 LTS
#>
#> Matrix products: default
#> BLAS: /home/biocbuild/bbs-3.22-bioc/R/lib/libRblas.so
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_GB LC_COLLATE=C
#> [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: America/New_York
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] Seurat_5.3.0 SeuratObject_5.2.0
#> [3] sp_2.2-0 stringr_1.5.2
#> [5] scuttle_1.19.0 scRNAseq_2.23.0
#> [7] SingleCellExperiment_1.31.1 SummarizedExperiment_1.39.2
#> [9] Biobase_2.69.1 GenomicRanges_1.61.4
#> [11] Seqinfo_0.99.2 IRanges_2.43.2
#> [13] S4Vectors_0.47.2 BiocGenerics_0.55.1
#> [15] generics_0.1.4 MatrixGenerics_1.21.0
#> [17] matrixStats_1.5.0 patchwork_1.3.2
#> [19] ggplot2_4.0.0 CSOA_0.99.2
#> [21] BiocStyle_2.37.1
#>
#> loaded via a namespace (and not attached):
#> [1] ProtGenerics_1.41.0 spatstat.sparse_3.1-0 bitops_1.0-9
#> [4] httr_1.4.7 RColorBrewer_1.1-3 tools_4.5.1
#> [7] sctransform_0.4.2 alabaster.base_1.9.5 R6_2.6.1
#> [10] HDF5Array_1.37.0 lazyeval_0.2.2 uwot_0.2.3
#> [13] rhdf5filters_1.21.0 withr_3.0.2 gridExtra_2.3
#> [16] progressr_0.16.0 cli_3.6.5 spatstat.explore_3.5-2
#> [19] fastDummies_1.7.5 labeling_0.4.3 alabaster.se_1.9.0
#> [22] sass_0.4.10 S7_0.2.0 spatstat.data_3.1-8
#> [25] ggridges_0.5.7 pbapply_1.7-4 Rsamtools_2.25.3
#> [28] dichromat_2.0-0.1 parallelly_1.45.1 RSQLite_2.4.3
#> [31] RApiSerialize_0.1.4 BiocIO_1.19.0 ica_1.0-3
#> [34] spatstat.random_3.4-1 dplyr_1.1.4 wesanderson_0.3.7
#> [37] Matrix_1.7-4 ggbeeswarm_0.7.2 abind_1.4-8
#> [40] lifecycle_1.0.4 yaml_2.3.10 rhdf5_2.53.4
#> [43] SparseArray_1.9.1 BiocFileCache_2.99.6 Rtsne_0.17
#> [46] grid_4.5.1 blob_1.2.4 promises_1.3.3
#> [49] ExperimentHub_2.99.5 crayon_1.5.3 miniUI_0.1.2
#> [52] lattice_0.22-7 beachmat_2.25.5 cowplot_1.2.0
#> [55] GenomicFeatures_1.61.6 KEGGREST_1.49.1 poibin_1.6
#> [58] magick_2.9.0 pillar_1.11.1 knitr_1.50
#> [61] rjson_0.2.23 bayesbio_1.0.0 future.apply_1.20.0
#> [64] codetools_0.2-20 glue_1.8.0 spatstat.univar_3.1-4
#> [67] data.table_1.17.8 vctrs_0.6.5 png_0.1-8
#> [70] gypsum_1.5.0 spam_2.11-1 gtable_0.3.6
#> [73] kernlab_0.9-33 cachem_1.1.0 xfun_0.53
#> [76] S4Arrays_1.9.1 mime_0.13 tidygraph_1.3.1
#> [79] survival_3.8-3 tinytex_0.57 fitdistrplus_1.2-4
#> [82] ROCR_1.0-11 nlme_3.1-168 bit64_4.6.0-1
#> [85] alabaster.ranges_1.9.1 filelock_1.0.3 RcppAnnoy_0.0.22
#> [88] GenomeInfoDb_1.45.11 bslib_0.9.0 irlba_2.3.5.1
#> [91] vipor_0.4.7 KernSmooth_2.23-26 DBI_1.2.3
#> [94] ggrastr_1.0.2 tidyselect_1.2.1 bit_4.6.0
#> [97] compiler_4.5.1 curl_7.0.0 httr2_1.2.1
#> [100] h5mread_1.1.1 textshape_1.7.5 DelayedArray_0.35.3
#> [103] plotly_4.11.0 stringfish_0.17.0 bookdown_0.44
#> [106] rtracklayer_1.69.1 scales_1.4.0 lmtest_0.9-40
#> [109] ggeasy_0.1.6 sgof_2.3.5 rappdirs_0.3.3
#> [112] digest_0.6.37 goftest_1.2-3 spatstat.utils_3.1-5
#> [115] alabaster.matrix_1.9.0 rmarkdown_2.29 XVector_0.49.1
#> [118] htmltools_0.5.8.1 pkgconfig_2.0.3 ensembldb_2.33.2
#> [121] dbplyr_2.5.1 fastmap_1.2.0 UCSC.utils_1.5.0
#> [124] rlang_1.1.6 htmlwidgets_1.6.4 shiny_1.11.1
#> [127] farver_2.1.2 jquerylib_0.1.4 zoo_1.8-14
#> [130] jsonlite_2.0.0 BiocParallel_1.43.4 RCurl_1.98-1.17
#> [133] magrittr_2.0.4 dotCall64_1.2 Rhdf5lib_1.31.0
#> [136] Rcpp_1.1.0 kerntools_1.2.0 ggnewscale_0.5.2
#> [139] viridis_0.6.5 reticulate_1.43.0 stringi_1.8.7
#> [142] alabaster.schemas_1.9.0 ggraph_2.2.2 MASS_7.3-65
#> [145] AnnotationHub_3.99.6 plyr_1.8.9 parallel_4.5.1
#> [148] listenv_0.9.1 ggrepel_0.9.6 deldir_2.0-4
#> [151] Biostrings_2.77.2 graphlayouts_1.2.2 splines_4.5.1
#> [154] tensor_1.5.1 igraph_2.1.4 spatstat.geom_3.5-0
#> [157] RcppHNSW_0.6.0 reshape2_1.4.4 BiocVersion_3.22.0
#> [160] XML_3.99-0.19 evaluate_1.0.5 RcppParallel_5.1.11-1
#> [163] BiocManager_1.30.26 tweenr_2.0.3 httpuv_1.6.16
#> [166] RANN_2.6.2 tidyr_1.3.1 purrr_1.1.0
#> [169] polyclip_1.10-7 alabaster.sce_1.9.0 qs_0.27.3
#> [172] future_1.67.0 scattermore_1.2 ggforce_0.5.0
#> [175] xtable_1.8-4 AnnotationFilter_1.33.0 restfulr_0.0.16
#> [178] RSpectra_0.16-2 later_1.4.4 viridisLite_0.4.2
#> [181] tibble_3.3.0 beeswarm_0.4.0 memoise_2.0.1
#> [184] AnnotationDbi_1.71.1 GenomicAlignments_1.45.4 cluster_2.1.8.1
#> [187] globals_0.18.0