Here, we take a look at two different datasets containing both DNA accessibility measurements and mitochondrial mutation data in the same cells. One was sampled from a patient with a colorectal cancer (CRC) tumor, and the other is from a polyclonal TF1 cell line. This data was produced by Lareau and Ludwig et al. (2020), and you can read the original paper here: https://doi.org/10.1038/s41587-020-0645-6.

Processed data files, including mitochondrial variant data for the CRC and TF1 dataset is available on Zenodo here: https://zenodo.org/record/3977808

Raw sequencing data and DNA accessibility processed files for the these datasets are available on NCBI GEO here: https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE142745

View data download code

The required files can be downloaded by running the following lines in a shell:

# ATAC data
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.filtered_peak_bc_matrix.h5
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.singlecell.csv
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.fragments.tsv.gz
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.fragments.tsv.gz.tbi

# mitochondrial allele data
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.A.txt.gz
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.C.txt.gz
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.G.txt.gz
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.T.txt.gz
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.depthTable.txt
wget https://zenodo.org/record/3977808/files/CRC_v12-mtMask_mgatk.chrM_refAllele.txt

Colorectal cancer dataset

To demonstrate combined analyses of mitochondrial DNA variants and accessible chromatin, we’ll walk through a vignette analyzing cells from a primary colorectal adenocarcinoma. The sample contains a mixture of malignant epithelial cells and tumor infiltrating immune cells.

Loading the DNA accessibility data

First we load the scATAC-seq data and create a Seurat object following the standard workflow for scATAC-seq data.

# load counts and metadata from cellranger-atac
counts <- Read10X_h5(filename = "../vignette_data/mito/CRC_v12-mtMask_mgatk.filtered_peak_bc_matrix.h5")
metadata <- read.csv(
  file = "../vignette_data/mito/CRC_v12-mtMask_mgatk.singlecell.csv",
  header = TRUE,
  row.names = 1
)

# load gene annotations from Ensembl
annotations <- GetGRangesFromEnsDb(ensdb = EnsDb.Hsapiens.v75)

# change to UCSC style since the data was mapped to hg19
seqlevels(annotations) <- paste0('chr', seqlevels(annotations))
genome(annotations) <- "hg19"

# create object
crc_assay <- CreateChromatinAssay(
  counts = counts,
  sep = c(":", "-"),
  annotation = annotations,
  min.cells = 10,
  fragments = '../vignette_data/mito/CRC_v12-mtMask_mgatk.fragments.tsv.gz'
)
crc <- CreateSeuratObject(
  counts = crc_assay,
  assay = 'peaks',
  meta.data = metadata
)

crc[["peaks"]]
## ChromatinAssay data with 81787 features for 3535 cells
## Variable features: 0 
## Genome: 
## Annotation present: TRUE 
## Motifs present: FALSE 
## Fragment files: 1

Quality control

We can compute the standard quality control metrics for scATAC-seq and filter out low-quality cells based on these metrics.

# Augment QC metrics that were computed by cellranger-atac
crc$pct_reads_in_peaks <- crc$peak_region_fragments / crc$passed_filters * 100
crc$pct_reads_in_DNase <- crc$DNase_sensitive_region_fragments / crc$passed_filters * 100
crc$blacklist_ratio <- crc$blacklist_region_fragments / crc$peak_region_fragments

# compute TSS enrichment score and nucleosome banding pattern
crc <- TSSEnrichment(crc)
crc <- NucleosomeSignal(crc)
# visualize QC metrics for each cell
VlnPlot(crc, c("TSS.enrichment", "nCount_peaks", "nucleosome_signal", "pct_reads_in_peaks", "pct_reads_in_DNase", "blacklist_ratio"), pt.size = 0, ncol = 3)

# remove low-quality cells
crc <- subset(
  x = crc,
  subset = nCount_peaks > 1000 &
    nCount_peaks < 50000 &
    pct_reads_in_DNase > 40 &
    blacklist_ratio < 0.05 &
    TSS.enrichment > 3 & 
    nucleosome_signal < 4
)
crc
## An object of class Seurat 
## 81787 features across 1861 samples within 1 assay 
## Active assay: peaks (81787 features, 0 variable features)
##  2 layers present: counts, data

Loading the mitochondrial variant data

Next we can load the mitochondrial DNA variant data for these cells that was quantified using mgatk. The ReadMGATK() function in Signac allows the output from mgatk to be read directly into R in a convenient format for downstream analysis with Signac. Here, we load the data and add it to the Seurat object as a new assay.

# load mgatk output
mito.data <- ReadMGATK(dir = "../vignette_data/mito/crc/")

# create an assay
mito <- CreateAssayObject(counts = mito.data$counts)

# Subset to cell present in the scATAC-seq assat
mito <- subset(mito, cells = Cells(crc))

# add assay and metadata to the seurat object
crc[["mito"]] <- mito
crc <- AddMetaData(crc, metadata = mito.data$depth[Cells(mito), ], col.name = "mtDNA_depth")

We can look at the mitochondrial sequencing depth for each cell, and further subset the cells based on mitochondrial sequencing depth.

VlnPlot(crc, "mtDNA_depth", pt.size = 0.1) + scale_y_log10()

# filter cells based on mitochondrial depth
crc <- subset(crc, mtDNA_depth >= 10)
crc
## An object of class Seurat 
## 214339 features across 1359 samples within 2 assays 
## Active assay: peaks (81787 features, 0 variable features)
##  2 layers present: counts, data
##  1 other assay present: mito

Dimension reduction and clustering

Next we can run a standard dimension reduction and clustering workflow using the scATAC-seq data to identify cell clusters.

crc <- RunTFIDF(crc)
crc <- FindTopFeatures(crc, min.cutoff = 10)
crc <- RunSVD(crc)
crc <- RunUMAP(crc, reduction = "lsi", dims = 2:50)
crc <- FindNeighbors(crc, reduction = "lsi", dims = 2:50)
crc <- FindClusters(crc, resolution = 0.7, algorithm = 3)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 1359
## Number of edges: 63301
## 
## Running smart local moving algorithm...
## Maximum modularity in 10 random starts: 0.7329
## Number of communities: 6
## Elapsed time: 0 seconds
DimPlot(crc, label = TRUE) + NoLegend()

Generate gene scores

To help interpret these clusters of cells, and assign a cell type label, we’ll estimate gene activities by summing the DNA accessibility in the gene body and promoter region.

# compute gene accessibility
gene.activities <- GeneActivity(crc)

# add to the Seurat object as a new assay
crc[['RNA']] <- CreateAssayObject(counts = gene.activities)

crc <- NormalizeData(
  object = crc,
  assay = 'RNA',
  normalization.method = 'LogNormalize',
  scale.factor = median(crc$nCount_RNA)
)

Visualize interesting gene activity scores

We note the following markers for different cell types in the CRC dataset:

  • EPCAM is a marker for epithelial cells
  • TREM1 is a meyloid marker
  • PTPRC = CD45 is a pan-immune cell marker
  • IL1RL1 is a basophil marker
  • GATA3 is a Tcell maker
DefaultAssay(crc) <- 'RNA'

FeaturePlot(
  object = crc,
  features = c('TREM1', 'EPCAM', "PTPRC", "IL1RL1","GATA3", "KIT"),
  pt.size = 0.1,
  max.cutoff = 'q95',
  ncol = 2
)

Using these gene score values, we can assign cluster identities:

crc <- RenameIdents(
  object = crc,
  '0' = 'Epithelial',
  '1' = 'Epithelial',
  '2' = 'Basophil',
  '3' = 'Myeloid_1',
  '4' = 'Myeloid_2',
  '5' = 'Tcell'
)

One of the myeloid clusters has a lower percentage of fragments in peaks, as well as a lower overall mitochondrial sequencing depth and a different nucleosome banding pattern.

p1 <- FeatureScatter(crc, "mtDNA_depth", "pct_reads_in_peaks") + ggtitle("") + scale_x_log10()
p2 <- FeatureScatter(crc, "mtDNA_depth", "nucleosome_signal") + ggtitle("") + scale_x_log10()

p1 + p2 + plot_layout(guides = 'collect')

We can see that most of the low FRIP cells were the myeloid 1 cluster. This is most likely an intra-tumor granulocyte that has relatively poor accessible chromatin enrichment. Similarly, the unusual nuclear chromatin packaging of this cell type yields slightly reduced mtDNA coverage compared to the myeloid 2 cluster.

Find informative mtDNA variants

Next, we can identify sites in the mitochondrial genome that vary across cells, and cluster the cells into clonotypes based on the frequency of these variants in the cells. Signac utilizes the principles established in the original mtscATAC-seq work of identifying high-quality variants.

variable.sites <- IdentifyVariants(crc, assay = "mito", refallele = mito.data$refallele)
VariantPlot(variants = variable.sites)

The plot above clearly shows a group of variants with a higher VMR and strand concordance. In principle, a high strand concordance reduces the likelihood of the allele frequency being driven by sequencing error (which predominately occurs on one but not the other strand. This is due to the preceding nucleotide content and a common error in mtDNA genotyping). On the other hand, variants that have a high VMR are more likely to be clonal variants as the alternate alleles tend to aggregate in certain cells rather than be equivalently dispersed about all cells, which would be indicative of some other artifact.

We note that variants that have a very low VMR and and very high strand concordance are homoplasmic variants for this sample. While these may be interesting in some settings (e.g. donor demultiplexing), for inferring subclones, these are not particularly useful.

Based on these thresholds, we can filter out a set of informative mitochondrial variants that differ across the cells.

# Establish a filtered data frame of variants based on this processing
high.conf <- subset(
  variable.sites, subset = n_cells_conf_detected >= 5 &
    strand_correlation >= 0.65 &
    vmr > 0.01
)

high.conf[,c(1,2,5)]
##          position nucleotide      mean
## 1227G>A      1227        G>A 0.0090107
## 6081G>A      6081        G>A 0.0031485
## 9804G>A      9804        G>A 0.0035833
## 12889G>A    12889        G>A 0.0217851
## 9728C>T      9728        C>T 0.0150412
## 16147C>T    16147        C>T 0.6582835
## 824T>C        824        T>C 0.0050820
## 2285T>C      2285        T>C 0.0034535
## 16093T>C    16093        T>C 0.0093126

A few things stand out. First, 10 out of the 12 variants occur at less than 1% allele frequency in the population. However, 16147C>T is present at about 62%. We’ll see that this is a clonal variant marking the epithelial cells. Additionally, all of the called variants are transitions (A - G or C - T) rather than transversion mutations (A - T or C - G). This fits what we know about how these mutations arise in the mitochondrial genome.

Depending on your analytical question, these thresholds can be adjusted to identify variants that are more prevalent in other cells.

Compute the variant allele frequency for each cell

We currently have information for each strand stored in the mito assay to allow strand concordance to be assessed. Now that we have our set of high-confidence informative variants, we can create a new assay containing strand-collapsed allele frequency counts for each cell for these variants using the AlleleFreq() function.

crc <- AlleleFreq(
  object = crc,
  variants = high.conf$variant,
  assay = "mito"
)
crc[["alleles"]]
## Assay data with 9 features for 1359 cells
## First 9 features:
##  1227G>A, 6081G>A, 9804G>A, 12889G>A, 9728C>T, 16147C>T, 824T>C,
## 2285T>C, 16093T>C

Visualize the variants

Now that the allele frequencies are stored as an additional assay, we can use the standard functions in Seurat to visualize how these allele frequencies are distributed across the cells. Here we visualize a subset of the variants using FeaturePlot() and DoHeatmap().

DefaultAssay(crc) <- "alleles"
alleles.view <- c("12889G>A", "16147C>T", "9728C>T", "9804G>A")
FeaturePlot(
  object = crc,
  features = alleles.view,
  order = TRUE,
  cols = c("grey", "darkred"),
  ncol = 4
) & NoLegend()

DoHeatmap(crc, features = rownames(crc), slot = "data", disp.max = 1) +
  scale_fill_viridis_c()

Here, we can see a few interesting patterns for the selected variants. 16147C>T is present in essentially all epithelial cells and almost exclusively in epithelial cells (the edge cases where this isn’t true are also cases where the UMAP and clustering don’t full agree). It is at 100% allele frequency– strongly suggestive of whatever cell of origin of this tumor had the mutation at 100% and then expanded. We then see at least 3 variants 1227G>A, 12889G>A, and 9728C>T that are mostly present specifically in the epithelial cells that define subclones. Other variants including 3244G>A, 9804G>A, and 824T>C are found specifically in immune cell populations, suggesting that these arose from a common hematopoetic progenitor cell (probably in the bone marrow).

TF1 cell line dataset

Next we’ll demonstrate a similar workflow to identify cell clones in a different dataset, this time generated from a TF1 cell line. This dataset contains more clones present at a higher proportion, based on the experimental design.

We’ll demonstrate how to identify groups of related cells (clones) by clustering the allele frequency data and how to relate these clonal groups to accessibility differences utilizing the multimodal capabilities of Signac.

Data loading

View data download code

To download the data from Zenodo run the following in a shell:

# ATAC data
wget https://zenodo.org/record/3977808/files/TF1.filtered.fragments.tsv.gz
wget https://zenodo.org/record/3977808/files/TF1.filtered.fragments.tsv.gz.tbi
wget https://zenodo.org/record/3977808/files/TF1.filtered.narrowPeak.gz

# mitochondrial genome data
wget https://zenodo.org/record/3977808/files/TF1_filtered.A.txt.gz
wget https://zenodo.org/record/3977808/files/TF1_filtered.T.txt.gz
wget https://zenodo.org/record/3977808/files/TF1_filtered.C.txt.gz
wget https://zenodo.org/record/3977808/files/TF1_filtered.G.txt.gz
wget https://zenodo.org/record/3977808/files/TF1_filtered.chrM_refAllele.txt.gz
wget https://zenodo.org/record/3977808/files/TF1_filtered.depthTable.txt.gz
# read the mitochondrial data
tf1.data <- ReadMGATK(dir = "../vignette_data/mito/tf1/")
## Reading allele counts
## Reading metadata
## Building matrices
# create a Seurat object
tf1 <- CreateSeuratObject(
  counts = tf1.data$counts,
  meta.data = tf1.data$depth,
  assay = "mito"
)

# load the peak set
peaks <- read.table(
  file = "../vignette_data/mito/TF1.filtered.narrowPeak.gz",
  sep = "\t",
  col.names = c("chrom", "start", "end", "peak", "width", "strand", "x", "y", "z", "w")
)
peaks <- makeGRangesFromDataFrame(peaks)

# create fragment object
frags <- CreateFragmentObject(
  path = "../vignette_data/mito/TF1.filtered.fragments.tsv.gz",
  cells = colnames(tf1)
)
## Computing hash
# quantify the DNA accessibility data
counts <- FeatureMatrix(
  fragments = frags,
  features = peaks,
  cells = colnames(tf1)
)
## Extracting reads overlapping genomic regions
# create assay with accessibility data and add it to the Seurat object
tf1[["peaks"]] <- CreateChromatinAssay(
  counts = counts,
  fragments = frags
)

Quality control

# add annotations
Annotation(tf1[["peaks"]]) <- annotations
DefaultAssay(tf1) <- "peaks"

tf1 <- NucleosomeSignal(tf1)
tf1 <- TSSEnrichment(tf1)
VlnPlot(tf1, c("nCount_peaks", "nucleosome_signal", "TSS.enrichment"), pt.size = 0.1)

tf1 <- subset(
  x = tf1,
  subset = nCount_peaks > 500 &
    nucleosome_signal < 2 &
    TSS.enrichment > 2.5
)
tf1
## An object of class Seurat 
## 255300 features across 832 samples within 2 assays 
## Active assay: peaks (122748 features, 0 variable features)
##  2 layers present: counts, data
##  1 other assay present: mito

Identifying variants

DefaultAssay(tf1) <- "mito"
variants <- IdentifyVariants(tf1, refallele = tf1.data$refallele)
## Computing total coverage per base
## Processing A
## Processing T
## Processing C
## Processing G
VariantPlot(variants)

high.conf <- subset(
  variants, subset = n_cells_conf_detected >= 5 &
    strand_correlation >= 0.65 &
    vmr > 0.01
)
tf1 <- AlleleFreq(tf1, variants = high.conf$variant, assay = "mito")
tf1[["alleles"]]
## Assay data with 51 features for 832 cells
## First 10 features:
##  627G>A, 709G>A, 1045G>A, 1793G>A, 1888G>A, 1906G>A, 2002G>A, 2040G>A,
## 2573G>A, 2643G>A

Identifying clones

Now that we’ve identified a set of variable alleles, we can cluster the cells based on the frequency of each of these alleles using the FindClonotypes() function. This uses the Louvain community detection algorithm implemented in Seurat.

DefaultAssay(tf1) <- "alleles"
tf1 <- FindClonotypes(tf1)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 832
## Number of edges: 15680
## 
## Running smart local moving algorithm...
## Maximum modularity in 10 random starts: 0.8398
## Number of communities: 12
## Elapsed time: 0 seconds
## 
##  10  11   9   4   7   8   2   3   1   5   0   6 
##  17  11  23 107  32  30 116 107 123  80 153  33

Here we see that the clonal clustering has identified 12 different clones in the TF1 dataset. We can further visualize the frequency of alleles in these clones using DoHeatmap(). The FindClonotypes() function also performs hierarchical clustering on both the clonotypes and the alleles, and sets the factor levels for the clonotypes based on the hierarchical clustering order, and the order of variable features based on the hierarchical feature clustering. This allows us to get a decent ordering of both features and clones automatically:

DoHeatmap(tf1, features = VariableFeatures(tf1), slot = "data", disp.max = 0.1) +
  scale_fill_viridis_c()

Find differentially accessible peaks between clones

Next we can use the clonal information derived from the mitochondrial assay to find peaks that are differentially accessible between clones.

DefaultAssay(tf1) <- "peaks"

# find peaks specific to one clone
markers.fast <- FoldChange(tf1, ident.1 = 2)
markers.fast <- markers.fast[order(markers.fast$avg_log2FC, decreasing = TRUE), ] # sort by fold change
head(markers.fast)
##                          avg_log2FC pct.1 pct.2
## chr5-42811975-42812177     3.801568 0.164 0.014
## chr3-4061278-4061591       3.725423 0.172 0.018
## chr5-44130972-44131478     3.666023 0.267 0.038
## chr5-43874930-43875314     3.447547 0.284 0.029
## chr5-42591230-42591506     3.413157 0.172 0.014
## chr6-114906484-114906735   3.271312 0.147 0.018

We can the DNA accessibility in these regions for each clone using the CoveragePlot() function. As you can see, the peaks identified are highly specific to one clone.

CoveragePlot(
  object = tf1,
  region = rownames(markers.fast)[1],
  extend.upstream = 2000,
  extend.downstream = 2000
)
## Warning: Removed 47 rows containing missing values or values outside the scale range
## (`geom_segment()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_segment()`).
Session Info
## R version 4.3.1 (2023-06-16)
## Platform: aarch64-apple-darwin20 (64-bit)
## Running under: macOS Sonoma 14.4.1
## 
## 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] C/UTF-8/C/C/C/C
## 
## time zone: Asia/Singapore
## tzcode source: internal
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] EnsDb.Hsapiens.v75_2.99.0 ensembldb_2.26.0         
##  [3] AnnotationFilter_1.26.0   GenomicFeatures_1.54.3   
##  [5] AnnotationDbi_1.64.1      Biobase_2.62.0           
##  [7] GenomicRanges_1.54.1      GenomeInfoDb_1.38.7      
##  [9] IRanges_2.36.0            S4Vectors_0.40.2         
## [11] BiocGenerics_0.48.1       patchwork_1.2.0          
## [13] ggplot2_3.5.0             Seurat_5.0.3             
## [15] SeuratObject_5.0.1        sp_2.1-3                 
## [17] Signac_1.13.0            
## 
## loaded via a namespace (and not attached):
##   [1] fs_1.6.3                    ProtGenerics_1.34.0        
##   [3] matrixStats_1.2.0           spatstat.sparse_3.0-3      
##   [5] bitops_1.0-7                httr_1.4.7                 
##   [7] RColorBrewer_1.1-3          tools_4.3.1                
##   [9] sctransform_0.4.1           backports_1.4.1            
##  [11] utf8_1.2.4                  R6_2.5.1                   
##  [13] lazyeval_0.2.2              uwot_0.1.16                
##  [15] withr_3.0.0                 prettyunits_1.2.0          
##  [17] gridExtra_2.3               progressr_0.14.0           
##  [19] cli_3.6.2                   textshaping_0.3.7          
##  [21] spatstat.explore_3.2-7      fastDummies_1.7.3          
##  [23] labeling_0.4.3              sass_0.4.9                 
##  [25] spatstat.data_3.0-4         ggridges_0.5.6             
##  [27] pbapply_1.7-2               pkgdown_2.0.7              
##  [29] Rsamtools_2.18.0            systemfonts_1.0.6          
##  [31] foreign_0.8-86              dichromat_2.0-0.1          
##  [33] parallelly_1.37.1           BSgenome_1.70.2            
##  [35] rstudioapi_0.16.0           RSQLite_2.3.5              
##  [37] generics_0.1.3              BiocIO_1.12.0              
##  [39] ica_1.0-3                   spatstat.random_3.2-3      
##  [41] dplyr_1.1.4                 Matrix_1.6-5               
##  [43] ggbeeswarm_0.7.2            fansi_1.0.6                
##  [45] abind_1.4-5                 lifecycle_1.0.4            
##  [47] yaml_2.3.8                  SummarizedExperiment_1.32.0
##  [49] SparseArray_1.2.4           BiocFileCache_2.10.1       
##  [51] Rtsne_0.17                  grid_4.3.1                 
##  [53] blob_1.2.4                  promises_1.2.1             
##  [55] crayon_1.5.2                miniUI_0.1.1.1             
##  [57] lattice_0.22-6              cowplot_1.1.3              
##  [59] KEGGREST_1.42.0             pillar_1.9.0               
##  [61] knitr_1.45                  rjson_0.2.21               
##  [63] future.apply_1.11.2         codetools_0.2-19           
##  [65] fastmatch_1.1-4             leiden_0.4.3.1             
##  [67] glue_1.7.0                  data.table_1.15.4          
##  [69] vctrs_0.6.5                 png_0.1-8                  
##  [71] spam_2.10-0                 gtable_0.3.4               
##  [73] cachem_1.0.8                xfun_0.43                  
##  [75] S4Arrays_1.2.1              mime_0.12                  
##  [77] survival_3.5-8              RcppRoll_0.3.0             
##  [79] fitdistrplus_1.1-11         ROCR_1.0-11                
##  [81] lsa_0.73.3                  nlme_3.1-164               
##  [83] bit64_4.0.5                 progress_1.2.3             
##  [85] filelock_1.0.3              RcppAnnoy_0.0.22           
##  [87] SnowballC_0.7.1             bslib_0.6.2                
##  [89] irlba_2.3.5.1               vipor_0.4.7                
##  [91] KernSmooth_2.23-22          rpart_4.1.23               
##  [93] colorspace_2.1-0            DBI_1.2.2                  
##  [95] Hmisc_5.1-2                 nnet_7.3-19                
##  [97] ggrastr_1.0.2               tidyselect_1.2.1           
##  [99] bit_4.0.5                   compiler_4.3.1             
## [101] curl_5.2.1                  htmlTable_2.4.2            
## [103] hdf5r_1.3.10                xml2_1.3.6                 
## [105] desc_1.4.3                  DelayedArray_0.28.0        
## [107] plotly_4.10.4               rtracklayer_1.62.0         
## [109] checkmate_2.3.1             scales_1.3.0               
## [111] lmtest_0.9-40               rappdirs_0.3.3             
## [113] stringr_1.5.1               digest_0.6.35              
## [115] goftest_1.2-3               spatstat.utils_3.0-4       
## [117] rmarkdown_2.26              XVector_0.42.0             
## [119] htmltools_0.5.8             pkgconfig_2.0.3            
## [121] base64enc_0.1-3             MatrixGenerics_1.14.0      
## [123] highr_0.10                  dbplyr_2.5.0               
## [125] fastmap_1.1.1               rlang_1.1.3                
## [127] htmlwidgets_1.6.4           shiny_1.8.1                
## [129] farver_2.1.1                jquerylib_0.1.4            
## [131] zoo_1.8-12                  jsonlite_1.8.8             
## [133] BiocParallel_1.36.0         VariantAnnotation_1.48.1   
## [135] RCurl_1.98-1.14             magrittr_2.0.3             
## [137] Formula_1.2-5               GenomeInfoDbData_1.2.11    
## [139] dotCall64_1.1-1             munsell_0.5.0              
## [141] Rcpp_1.0.12                 reticulate_1.35.0          
## [143] stringi_1.8.3               zlibbioc_1.48.0            
## [145] MASS_7.3-60.0.1             plyr_1.8.9                 
## [147] parallel_4.3.1              listenv_0.9.1              
## [149] ggrepel_0.9.5               deldir_2.0-4               
## [151] Biostrings_2.70.2           splines_4.3.1              
## [153] tensor_1.5                  hms_1.1.3                  
## [155] igraph_2.0.3                spatstat.geom_3.2-9        
## [157] RcppHNSW_0.6.0              reshape2_1.4.4             
## [159] biomaRt_2.58.2              XML_3.99-0.16.1            
## [161] evaluate_0.23               biovizBase_1.50.0          
## [163] httpuv_1.6.15               RANN_2.6.1                 
## [165] tidyr_1.3.1                 purrr_1.0.2                
## [167] polyclip_1.10-6             future_1.33.2              
## [169] scattermore_1.2             xtable_1.8-4               
## [171] restfulr_0.0.15             RSpectra_0.16-1            
## [173] later_1.3.2                 viridisLite_0.4.2          
## [175] ragg_1.3.0                  tibble_3.2.1               
## [177] memoise_2.0.1               beeswarm_0.4.0             
## [179] GenomicAlignments_1.38.2    cluster_2.1.6              
## [181] globals_0.16.3