Traditional analysis of single-cell TCR sequencing (scTCR-seq) data defines T cell clones based on the paired CDR3 sequences of the TCRα and TCRβ chains. However, as >50% of cells in some samples possess only 1 or >2 TCR chains, this approach is ill-suited to interpret the clonotypes of a substantial proportion of cells.
ECLIPSE, building off of VDJdive,
was developed to address this limitation of standard scTCR-seq analysis.
By analyzing chain pairings across a sample, it is able to resolve
clonal ambiguity, allowing for the prediction of chains lost of dropout
and the deciphering of true biological expression of extra TCR chains
from technical artifacts. With less missing data and larger clone sizes,
users are then able to make best use of their scTCR-seq data. The
ECLIPSE workflow is also quite streamlined, typically utilizing just 2
core functions (ECLIPSE –>
tcrDoubletDetect). After completing these two functions,
users are ready for convenient downstream analysis with the scRepertoire
package.
This vignette will walk through the basic workflow of the ECLIPSE package. Please refer to our pre-print for more details about this method.
We need to first load three packages to run the vignette:
library(remotes) # only needed to insall the package
library(tidyverse) # needed for data wrangling (or use base R instead)
library(scRepertoire) # needed for downstream analysisPlease use the following code to download ECLIPSE from
GitHub and load it:
ECLIPSE includes a small test dataset of 1,000 cells from 2 patients (S3 and S15) with clear cell renal cell carcinoma (derived from Braun et al., 2021). Each patient has two samples: one from tumor tissue and one from matched adjacent normal tissue. To load the dataset in, we can use the following code:
ECLIPSE2 main inputs are required for ECLIPSE: a Seurat object
that contains scRNA-seq data and contig files from
cellranger vdj/multi (preferably
all_contig_annotations.csv files).
The first input is contig files from cellranger multi or
cellranger vdj, specifically
all_contig_annotations.csv or
filtered_contig_annotations.csv files. We strongly
recommend using all_contig_annotations files as the filtered
contig files remove some biologically true contigs (ex. TCRα chains that
use the TRDV1 gene segment, see here) as well
as non-productive contigs. While these non-productive chains may not
influence T cell antigen specificity, their presence in the contig files
allows for enhanced fidelity
of clonal tracking by ECLIPSE.
To feed the contig files into ECLIPSE, there are two
options:
For this option, a vector of folder paths that contain contig files
is needed. ECLIPSE will find the contig files within the
folders you provide, however it requires that each folder only contains
1 set of contig files (i.e. you cannot put the contig files from each
sample into 1 folder). In order to create a vector of folder paths, we
can use the list.dirs() function:
# Finding where the vignette data for ECLIPSE is stored
vignette_dir <- system.file("extdata/vignette", package = "ECLIPSE")
# Generates the vector of folder paths and excludes the current directory
tcr_folder_paths <- list.dirs(path = vignette_dir, full.names = TRUE)[-1]
print(tcr_folder_paths)
#> [1] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S15N"
#> [2] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S15T"
#> [3] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S3N"
#> [4] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S3T"The second option also requires a vector of paths, but instead you
specify the full path to the contig file. This option would be better if
you have many contig files from different samples in one folder. To make
the vector, here we can use list.files():
# Generates the vector of file paths
contig_paths <- list.files(path = vignette_dir,
pattern = "all_contig",
recursive = TRUE,
full.names = TRUE)
contig_paths
#> [1] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S15N/all_contig_annotations.csv.gz"
#> [2] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S15T/all_contig_annotations.csv.gz"
#> [3] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S3N/all_contig_annotations.csv.gz"
#> [4] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S3T/all_contig_annotations.csv.gz"4 arguments of ECLIPSE must be provided column names
from the Seurat meta.data: original_barcode,
batch, donor, and group. We will
create these columns now.
original_barcodeThis column must contain the original 10X barcode for each cell.
Because many scRNA-seq analysis pipelines add prefixes/suffixes to cell
barcodes, this column is required so that the TCR contigs (which contain
the original 10X barcodes) can be matched to their respective cell in
the Seurat object. Typically, barcodes will end with -1, but this may
not always be true, such as if cellranger aggr was used.
Always check your raw contig and counts files to be sure!
For our dataset, the raw contig files have cell barcode suffixes of -1, so let’s look at the barcodes in our Seurat object to see if they will match:
# Existing cell barcodes. They aren't the original!
head(rownames(vignette_object[[]]))
#> [1] "CTAGTGATCAGCAACT-20" "CCACCTAGTGAGGCTA-20" "TTCTCAACACCCATTC-20"
#> [4] "CGTCAGGTCCCATTAT-20" "GGGTTGCTCCTCAATT-20" "ATTCTACCAAAGTCAA-20"As you can see, they do not match. Let’s change the suffixes to -1
and make a new column called cell_barcode that we can pass
to the original_barcode argument of ECLIPSE:
# Making a column with these barcodes where the suffix is -1
vignette_object$cell_barcode <- str_replace(rownames(vignette_object[[]]),
pattern = "-.*$",
replacement = "-1")
head(vignette_object$cell_barcode) %>% as.vector()
#> [1] "CTAGTGATCAGCAACT-1" "CCACCTAGTGAGGCTA-1" "TTCTCAACACCCATTC-1"
#> [4] "CGTCAGGTCCCATTAT-1" "GGGTTGCTCCTCAATT-1" "ATTCTACCAAAGTCAA-1"batchThis column tells ECLIPSE which contig file holds the
TCR information for each cell in the Seurat object. The position of the
contig file in the vector must be provided here (ex. 2, but not
“2”).
# Looking at the vector of file_paths
contig_paths
#> [1] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S15N/all_contig_annotations.csv.gz"
#> [2] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S15T/all_contig_annotations.csv.gz"
#> [3] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S3N/all_contig_annotations.csv.gz"
#> [4] "/tmp/Rtmp210Elj/Rinst1b8221977df0/ECLIPSE/extdata/vignette/S3T/all_contig_annotations.csv.gz"For example, contig_paths[1] holds a path for the TCR
contigs for a cells from the tumor tissue of S15, so any cell from that
sample must have 2 (integer) but not “2” (character) in this new column.
We can make this column, here called index, as such:
# Since normal tissue from sample 15 is first (S15N), it is 1.
# Tumor tissue from 15 is next (S15T), so it is 2, etc...
vignette_object[[]] <- vignette_object[[]] %>%
mutate(index = case_when(
Sample == "S15_N" ~ 1,
Sample == "S15_T" ~ 2,
Sample == "S3_N" ~ 3,
Sample == "S3_T" ~ 4))
# Checking the column
table(vignette_object$index)
#>
#> 1 2 3 4
#> 250 250 250 250donorNext, we need to tell ECLIPSE which donor (or mouse)
each cell was from. As ECLIPSE calls clonotypes by
utilizing chain pairing across a repertoire, the more cells that are
available, the more accurate this prediction will be (regardless of
time/condition/treatment). For this reason, a column is required to
provide the donor/mouse. Even if we want to compare S3 tumor vs. normal
and S15 tumor vs. normal downstream, here we must provide S3 and S15.
Fortunately, this column is already created for us, and it should be
already present in most real-world uses. Let’s look at this column,
called donor:
groupLastly, we need to tell ECLIPSE how we want the final
clonal statistics (i.e. clone cell count, frequency, etc..) to be
calculated after clones are called. For our example, either S15/S3 or
S3T/S3N/S15T/S15N would be equally valid. If a clone is only 1 cell,
then if we provide the donor, the frequency will be 1/500 vs. if we
provide the sample, then it will be 1/250. Please note that this
column can and often will be the same as the column
passed to donor. Let’s choose by sample here, which again is
already created for us through the column Sample (and
typically will be created in most real cases):
ECLIPSE functionNow we are ready to run the ECLIPSE function to analyze
our scTCR-seq data. Briefly, this function does the following:
findThreeChainClones to find clones with
biological expression of 3 total TCR chains and modifying the contigs to
address thisCTaa). If it is highly likely the cell is in 1 clone, then
that clone is used. If no clonotype is predicted with high confidence,
then clonal prediction is not performed, and the final clonotype is
instead determined by scRepertoire::combineTCR() using
parameters passed to ECLIPSE from the
combineTcrArgs argument (see supplemental information
section)For a full list of columns added by ECLIPSE and their description, please refer to the supplemental information section.
ECLIPSETo run ECLIPSE, there are two options, depending on
whether we generated a vector of folders containing contigs or contig
file paths. If we want to use a vector of folder paths, we can use the
following code:
# Option 1: Running with paths of folders containing contigs
object_after_ECLIPSE <- ECLIPSE(contig_type = "all",
contig_folder_paths = tcr_folder_paths,
seurat_object = vignette_object,
batch = "index",
donor = "donor",
group = "Sample",
original_barcode = "cell_barcode",
verbose = FALSE)Note that we use the contig_folder_paths argument, omit
the contig_file_paths argument, and
contig_type = "all". Together, this tells
ECLIPSE to search for
all_contig_annotations.csv files in the folders that we
provide.
If we want to provide TCR contig file paths, we can use the
contig_file_paths argument, omit the
contig_folder_paths argument, and
contig_type = "manual":
# Option 2: Running with contig paths
object_after_ECLIPSE <- ECLIPSE(contig_type = "manual",
contig_file_paths = contig_paths,
seurat_object = vignette_object,
batch = "index",
donor = "donor",
group = "Sample",
original_barcode = "cell_barcode")
#>
#> Reading TCR contig data in...
#> QC Filtering:
#> - 2682 initial contigs found before filtering
#> - 2682 contigs were from true cells (coming from 1000 cells)
#> - 0 contigs were removed for not being high confidence as defined by Cell Ranger
#> - 62 orphan contigs were found with no observed pairing. Cells containing these contigs are unlikely to have a clonotype predicted
#> - 622 contigs were removed for other reasons (ex. irregular CDR3, TCRγ/δ contig, duplicated, etc...)
#> - After all filtering, 2060 contigs from 1000 true cells remained
#>
#> Finding clones with extra chains...
#> Running donor 1/2 (S15):
#> 5 clones with an extra TCRα chain were detected, comprising 14.2% of cells (71 total cells)
#> 0 clones with an extra TCRβ chain were detected, comprising 0% of cells (0 total cells)
#>
#> Running donor 2/2 (S3):
#> 2 clones with an extra TCRα chain were detected, comprising 3.8% of cells (19 total cells)
#> 0 clones with an extra TCRβ chain were detected, comprising 0% of cells (0 total cells)
#>
#> Running VDJdive::clonoStats() for clone EM assignment...
#> Extracting results from VDJdive...
#> Finalizing clonal assignments...#> 5 cells were detected in clones that appear to be MAIT or iNKT cells
#> 84.1% of cells in the Seurat object were assigned a clonotype using high confidence prediction from VDJdive::clonoStats()
#> 100% of cells in the Seurat object were assigned a clonotype in the end (either from prediction or traditional analysis)
#> These final clonal annotations are stored in CTaa
#> Summary of the top 5 largest clones:
#> # A tibble: 5 × 4
#> # Groups: CTaa [5]
#> CTaa Sample Clone_Size Percent_of_Repertoire
#> <chr> <chr> <int> <dbl>
#> 1 CALGSNFGNEKLTF:::CGCKSYGKLTF_CASSQGLA… S15_N 47 18.8
#> 2 CAVQGSGGSYIPTF_CSARTLASGDNEQFF S15_N 40 16
#> 3 CAASMSGTYKYIF_CASSLAGTTQETQYF S3_T 30 12
#> 4 CAVGPDNYGNNRLAF_CASSIAPGTSGGYNEQFF S3_T 29 11.6
#> 5 CLVGAGSYQLTF_CASSPLDNSGNTIYF S15_T 25 10
As seen above, by default (verbose = TRUE), ECLIPSE will
print some key stats during filtering as well as a QC plot that shows
how well clonal prediction went. For this experiment, most cells (shown
in orange) received their final clonotype by prediction. Many of these
cells also were fully assigned to one clone by the EM algorithm (i.e. y
= 1), so overall the analysis went quite well.
Let’s take a look at the final clonotypes that ECLIPSE
decided on, now stored in the CTaa column of the Seurat
meta.data:
# Final ECLIPSE clonotype
head(object_after_ECLIPSE$CTaa) %>% as.vector()
#> [1] "CAASMSGTYKYIF_CASSLAGTTQETQYF" "NA_CASSLGGSYEQYF"
#> [3] "CAASMSGTYKYIF_CASSLAGTTQETQYF" "CAGFNAGNMLTF_CASSWGNEQFF"
#> [5] "CAASMSGTYKYIF_CASSLAGTTQETQYF" "CALNTGGFKTIF_CASSPRGPYNEQFF"We can also compare these final clonotypes to what traditional
analysis would have determined, now stored in the scR_CTaa
column:
# Clonotype from traditional analysis
head(object_after_ECLIPSE$scR_CTaa) %>% as.vector()
#> [1] "CAASMSGTYKYIF_CISVPAALGIRDRGNLEETQYF;CASSLAGTTQETQYF"
#> [2] "NA_CASSLGGSYEQYF"
#> [3] "CAASMSGTYKYIF_CASSLAGTTQETQYF"
#> [4] "CAGFNAGNMLTF_CSARDFADSGTSTDTQYF;CASSWGNEQFF"
#> [5] "CAASMSGTYKYIF_CASSLAGTTQETQYF"
#> [6] "CALNTGGFKTIF_CASSPRGPYNEQFF"ECLIPSE also provides clone sizes and clonal proportions in the
clonalFrequency and clonalProportion columns,
respectively, with matched columns having a scR_prefix to show what
would happen with traditional analysis. For a full list of columns added
by ECLIPSE, please refer to the supplemental information
section.
tcrDoubletDetectNow that the ECLIPSE function has been ran, we should
now run the 2nd core ECLIPSE function, tcrDoubletDetect, to
filter out cells with excessive TCR chains (these cells are likely a mix
of doublets and cells with TCR contamination). Here we remove any cells
with >2 TCRα chains, >2 TCRβ chains, or >4 total (α + β)
chains:
object_after_ECLIPSE <- tcrDoubletDetect(object_after_ECLIPSE,
singleChainLimit = 2,
totalChainLimit = 4)
#> 4 cells (0.4% of total) were removed due to having >2 TCRα or TCRβ chains originally
#> 0 cells (0% of total) were removed due to having >4 total chains originally
This function by default also prints plots that may be useful in setting
additional filtering thresholds. Here we can see that cells with 4 total
TCR chains have much higher RNA counts, so we may want to remove these
cells or any cells with ~ >7k nCount_RNA as they may be enriched for
doublets.
At this point we’re ready for downstream analysis. ECLIPSE generates
Seurat objects formatted for use with the popular scRepertoire
package, so functions introduced in that package typically can be used
here as well. One key thing to note is that since ECLIPSE
predicts clones based on CDR3 amino acid sequences, we must always use
the argument cloneCall = "aa" in scRepertoire
functions.
Shown below are a couple potential types of downstream analysis made possible with scRepertoire. For example, we can look at clonal sharing across samples:
clonalCompare(object_after_ECLIPSE,
top.clones = 10,
graph = "alluvial",
cloneCall = "aa",
group.by = "Sample") +
theme(legend.position = "none")We can also visualize which samples have a larger fraction of expanded clonotypes:
Or we can look at repertoire diversity across samples:
We hope this vignette is a helpful introduction to using ECLIPSE. If you have any questions, please email ethan.burns [@] yale.edu or use our issues page. For more details about our method, please refer to our pre-print.
findThreeChainClonesWhile not discussed above, ECLIPSE contains a third function that may
may be of use. This function was designed to help the
ECLIPSE function find clones with biological expression of
3 TCR chains, but can be also used to see which clones were called with
3 TCR chains and how that occurred. To see these clones we can use the
code below. First, we generate QC filtered contigs using the
returnFilteredContigs argument of ECLIPSE:
postqc_contigs <- ECLIPSE(contig_type = "all",
contig_folder_paths = tcr_folder_paths,
seurat_object = vignette_object,
batch = "index",
donor = "donor",
group = "Sample",
original_barcode = "cell_barcode",
verbose = FALSE,
returnFilteredContigs = TRUE)Next, we filter on a sample we are interested in, and pass the
contigs onto findThreeChainClones using
mode = "show" and chain = "a" to show clones
with 2 TCRα chains:
s15_contigs <- postqc_contigs %>% filter(donor == "S15")
three_chain_clones <- findThreeChainClones(s15_contigs,
chain = "a",
mode = "show",
printStats = TRUE)
#> Returning a list. The first table shows all beta chains predicted to have a double alpha pairing. The second table shows how/why many of these clones were called as such. Please note, the second table is pre-filtering, so not all clones may been in the first table since some may have been filtered out.This provides a list containing:
three_chain_clones[[1]]
#> # A tibble: 5 × 2
#> beta_chain predicted_double_alpha_pairing
#> <chr> <chr>
#> 1 CASSFMAGPYNEQFF CLPRLEYGNKLVF;CASPRGGGSQGNLIF
#> 2 CASSLEAAGVGEQYF CTTASWVPKAAGNKLTF;CAVGGSGNTPLVF
#> 3 CASSQGLAGLEETQYF CALGSNFGNEKLTF;CGCKSYGKLTF
#> 4 CASTTTDRGRGANVLTF CAVSNFGNEKLTF;CAVMTTDSWGKLQF
#> 5 CAWSGEGLAGVSYNEQFF CSERIFNKFYF;CALRGGGFKTIFthree_chain_clones[[2]]
#> total_cells cell_counts_per_alpha_pairing beta_chain
#> 1 38 26 11 1 CASSQGLAGLEETQYF
#> predicted_double_alpha_pairing observed_alpha_pairing1
#> 1 CALGSNFGNEKLTF;CGCKSYGKLTF CALGSNFGNEKLTF
#> observed_alpha_pairing2 observed_alpha_pairing3
#> 1 CALGSNFGNEKLTF;CGCKSYGKLTF CGCKSYGKLTFECLIPSE additional arguments| Argument | Description |
|---|---|
verbose |
By default TRUE. Prints stats such as QC
filtering efficacy, how many clones were called with 3 chains, and final
percentages of cells with a TCR called |
returnFilteredContigs |
By default FALSE. TRUE would
return the contigs after QC filtering |
allowDoubleChains |
By default "both", whereby clones can have
2 TCRα or TCRβ chains. If "alpha" or "beta" is
provided, only clones with 2 of that type will be permitted. If
"neither" is provided, then only clones with 2 chains will
be called |
combineTcrArgs |
A list that determines how
scRepertoire::combineTCR() is called. As
ECLIPSE runs combineTCR/combineExpression to
allow users to combine the predicted ECLIPSE clonotype with
a traditionally-defined clonotype, this tells which arguments
combineTCR should be run with. Specifically, it tells the 4
arguments 1) removeNA, 2) removeMulti, 3)
filterMulti, and 4) filterNonproductive in
that order. If ECLIPSE cannot call a clonotype with high
accuracy, the clonal annotation from this will be used as the final
clonotype. By default, this is
list(FALSE, FALSE, FALSE, TRUE), which calls
combineTCR with its default arguments (i.e. no
non-productive contigs are retained but clones with 1 TCR chain or >2
are allowed) |
detailedEclipseStats |
By default FALSE. This adds information
about the EM algorithm assignment to the Seurat object (i.e. what
proportion of the cell was assigned to the clone listed, etc…) |
format |
By default "Blank". This tells how missing
data is stored in contig files. Typically this is "" so
"Blank" should be specified, however older versions of Cell
Ranger use "None" instead. If this is the case, please
provide "None" here |
| Column | Description |
|---|---|
highConfChains |
all TCR chains that were sequenced in the cell and
passed QC filtering by ECLIPSE. Helpful to compare to final
clone (CTaa) used for analysis |
scR_CTgene, scR_CTnt,
scR_CTaa, scR_CTstrict,
scR_conalProportion, scR_clonalFrequency,
scR_cloneSize |
columns added by
scRepertoire::combineExpression, see scRepertoire
documentation for details. These provide information about the cell if
it was called with scRepertoire
and can be useful in comparing to the final clone (CTaa)
used for analysis. Please note the “scR_” prefix is added so ECLIPSE can
add other columns with the same name that can interact with
scRepertoire |
cloneCallSource |
whether the final clonotype list (in CTaa)
is from the prediction of VDJdive
or from traditional analysis from scRepertoire |
CTaa, clonalProportion,
clonalFrequency, cloneSize |
columns in the style of what would be added by
scRepertoire:::combineExpression. However,
CTaa here stores the final clonotype of the cell as
predicted by ECLIPSE |
cell_unconventional_subset,
clone_unconventional_subset |
cell_unconventional_subset tells if the
chains detected suggest the cell is a iNKT or MAIT cell.
clone_unconventional_subset gives similar information, but
uses the final clonotype of the cell instead of what chains were
detected in that particular cell (and therefore will capture more cells
as being a iNKT or MAIT cell). For both of these columns,
ECLIPSE searches for 3 subsets: 1) iNKT cells (must have
TRAV10 + TRAJ18 + TRBV25-1), 2) conventional MAIT cells (must have
TRAV1-2 + TRAJ33), and 3) unconventional MAIT cells (must have TRAV1-2 +
TRAJ12 or TRAV1-2 + TRAJ20. Please note that these are for human samples
only, murine T cells use different gene segments that are not searched
for by ECLIPSE |
EM_max_prop, EM_2ndmax_prop,
EM_max_2ndmax_ratio |
the proportion of the cell assigned by the EM algorithm
to the most and 2nd most likely clone. The ratio of these two
proportions (biggest/2nd biggest) is also provided. These columns are
only included if detailedEclipseStats == TRUE |
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] 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
#> [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: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] knitr_1.52 ECLIPSE_0.99.0 scRepertoire_2.9.1 lubridate_1.9.5
#> [5] forcats_1.0.1 stringr_1.6.0 dplyr_1.2.1 purrr_1.2.2
#> [9] readr_2.2.0 tidyr_1.3.2 tibble_3.3.1 ggplot2_4.0.3
#> [13] tidyverse_2.0.0 remotes_2.5.0 BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] gridExtra_2.3.1 testthat_3.3.2
#> [3] rlang_1.3.0 magrittr_2.0.5
#> [5] otel_0.2.0 matrixStats_1.5.0
#> [7] compiler_4.6.1 reshape2_1.4.5
#> [9] systemfonts_1.3.2 vctrs_0.7.3
#> [11] ggalluvial_0.12.6 gsl_2.1-9
#> [13] quantreg_6.1 pkgconfig_2.0.3
#> [15] fastmap_1.2.0 XVector_0.53.0
#> [17] labeling_0.4.3 ggraph_2.2.2
#> [19] utf8_1.2.6 rmarkdown_2.32
#> [21] tzdb_0.5.0 ragg_1.5.2
#> [23] MatrixModels_0.5-4 xfun_0.61
#> [25] cachem_1.1.0 jsonlite_2.0.0
#> [27] DelayedArray_0.39.8 BiocParallel_1.47.0
#> [29] tweenr_2.0.3 parallel_4.6.1
#> [31] R6_2.6.1 bslib_0.12.0
#> [33] stringi_1.8.9 RColorBrewer_1.1-3
#> [35] parallelly_1.48.0 brio_1.1.5
#> [37] GenomicRanges_1.65.4 jquerylib_0.1.4
#> [39] Rcpp_1.1.2 Seqinfo_1.3.2
#> [41] SummarizedExperiment_1.43.0 iNEXT_3.0.2
#> [43] future.apply_1.20.2 IRanges_2.47.5
#> [45] Matrix_1.7-6 splines_4.6.1
#> [47] igraph_2.3.4 timechange_0.4.0
#> [49] tidyselect_1.2.1 abind_1.4-8
#> [51] yaml_2.3.12 viridis_0.6.5
#> [53] codetools_0.2-20 listenv_1.1.0
#> [55] plyr_1.8.9 lattice_0.23-1
#> [57] Biobase_2.73.2 withr_3.0.3
#> [59] S7_0.2.2 evaluate_1.0.5
#> [61] survival_3.8-12 future_1.76.0
#> [63] polyclip_1.10-7 pillar_1.11.1
#> [65] BiocManager_1.30.27 MatrixGenerics_1.25.0
#> [67] stats4_4.6.1 generics_0.1.4
#> [69] sp_2.2-3 S4Vectors_0.51.10
#> [71] hms_1.1.4 scales_1.4.0
#> [73] globals_0.19.1 immApex_1.7.1
#> [75] glue_1.8.1 maketools_1.3.2
#> [77] tools_4.6.1 sys_3.4.3
#> [79] SparseM_1.84-2 buildtools_1.0.0
#> [81] graphlayouts_1.2.5 dotCall64_1.2
#> [83] cowplot_1.2.0 tidygraph_1.3.1
#> [85] grid_4.6.1 evmix_2.12
#> [87] SingleCellExperiment_1.35.2 ggforce_0.5.0
#> [89] VDJdive_1.15.3 cli_3.6.6
#> [91] textshaping_1.0.5 spam_2.11-4
#> [93] viridisLite_0.4.3 S4Arrays_1.13.2
#> [95] ggdendro_0.2.0 gtable_0.3.6
#> [97] sass_0.4.10 digest_0.6.39
#> [99] progressr_1.0.0 BiocGenerics_0.59.12
#> [101] SparseArray_1.13.4 ggrepel_0.9.8
#> [103] rjson_0.2.23 SeuratObject_5.4.0
#> [105] farver_2.1.2 memoise_2.0.1
#> [107] htmltools_0.5.9 lifecycle_1.0.5
#> [109] MASS_7.3-66