library(ClustIRR)
library(knitr)
library(visNetwork)
Adaptive immune immunity relies on diverse immune receptor repertoires (IRRs: B- and T-cell receptor repertoires) to protect the host against genetically diverse and rapidly evolving pathogens, such as viruses, bacteria, or cancers. The B- and T-cell receptor (BCR/TCR) sequence diversity originates in part due to V(D)J recombination, in which different germline-encoded genes are joined to form immune receptors. As a result of this process, practically every newly formed naive mature T cell and B cell is equipped with a distinct IR, and this allows them to recognize distinct sets of antigens.
B-cells bind antigens directly via the complementarity determining regions (CDR) of their BCRs, and T-cells recognize antigenic peptides presented by major histocompatibility (MHC) molecules via the CDRs of their TCRs. Antigen recognition may lead to B/T cell activation, and in such a case, the cells start to proliferate rapidly, forming antigen-specific clones that are capable of mounting effective immune response.
Recent studies have shown that similarity in TCR sequences implies shared antigen specificity between receptors. Hence, by clustering of TCR sequences of a repertoire derived by high-throughput sequencing (HT-seq), we can identify groups of TCRs with shared antigen specificity, which is essential for the development of cancer immunotherapies, vaccines, antiviral drugs, etc.
This vignette introduces ClustIRR, a computational method for clustering of IRRs.
ClustIRR is freely available as part of Bioconductor, filling the gap that currently exists in terms of software for quantitative analysis of IRRs.
To install ClustIRR please start R and enter:
if(!require("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("ClustIRR")
The algorithm of ClustIRR performs clustering of IRR sequences to find groups of receptors with similar specificity.
The main input of ClustIRR are two repertoires: s
and r
:
s
: IRR under investigation (case/sample)r
: reference IRR (control/reference)Both repertoires should be provided as data.frames. The rows in the data.frames correspond to clones (group of cells derived from a common parent cell by clonal expansion). ClustIRR requires the CDR3 sequence and the size of each clone.
CDR3 sequences
For each clone, s
and r
, must contain the amino acid sequences of the
corresponding complementarity determining regions 3 (CDR3s). We may have
the CDR3s from one T cell receptor chain (e.g., only CDR3\(\alpha\)s or
only CDR3\(\beta\)s) or from both chains (CDR3\(\alpha\beta\)). Similarly, we
may have CDR3s from \(\gamma\delta\) T-cells (CDR3\(\gamma\) and CDR3\(\delta\))
or B-cells (CDR3H and CDR3L). This is explained in case study 4.
clone size
s
and r
may also contain the size of each clone. This is provided by the
column clone_size
. If the clone sizes are not available, then we assume that
clone_size
= 1.
Let’s have a look at an example data set which we will use as input:
data("CDR3ab")
# take the first 500 CDR3b sequences from the data -> s
s <- data.frame(CDR3b = c(CDR3ab$CDR3b[1:500], "CASSSSPGDQEQFF"),
clone_size = 1)
# take 5000 CDR3b sequences 501:5500 from the data -> r
r <- data.frame(CDR3b = CDR3ab$CDR3b[501:5500],
clone_size = 1)
str(s)
#> 'data.frame': 501 obs. of 2 variables:
#> $ CDR3b : chr "CASTVTSGSNEKLFF" "CASSLTGTGYTF" "CSALTPGLIYNEQFF" "CSARASWGTNEKLFF" ...
#> $ clone_size: num 1 1 1 1 1 1 1 1 1 1 ...
str(r)
#> 'data.frame': 5000 obs. of 2 variables:
#> $ CDR3b : chr "CASSPRWGSGNTIYF" "CASSDTPNYGYTF" "CAWTPGTRIQETQYF" "CATSRGRYGKQFF" ...
#> $ clone_size: num 1 1 1 1 1 1 1 1 1 1 ...
ClustIRR employs two strategies for clustering:
s
compared to r
s
that have similar sequencesThe rationale behind these two clustering strategies is the following: two identical CDR3 sequences have the same specificity. Similar CDR3 sequences (e.g., CDR3 sequences that differ by only one amino acid) may have similar specificity. Global clustering is designed to find pairs of CDR3s that are globally (based on the complete CDR3 sequences) similar.
We also know that two CDR3s with significantly different sequences may still
recognize the same peptide1 Glanville, Jacob, et al. “Identifying specificity groups in the T cell
receptor repertoire.” Nature 547.7661 (2017): 94-98. if they share a motif in their core regions
(e.g., identical 4-mer). Such “useful” motifs may be enriched in s
but not
in r
, and local clustering aims to identify them.
CDR3 sequences are segmented into overlapping motifs (\(k\)-mers), where \(k\) is specified by setting the input \(ks = 4\).
Example of segmenting CDR3 sequence into 4-mers:
cdr3 <- "CASSTTTGTGELFF"
ks <- 4
colnames(stringdist::qgrams(cdr3, q = ks))
#> [1] "CASS" "SSTT" "STTT" "TTTG" "TTGT" "TGTG" "TGEL" "GTGE" "GELF" "ELFF"
#> [11] "ASST"
\(k\)-mers found in the core region of the CDR3 loop are more likely to
establish contact with an antigenic peptide than the \(k\)-mers found at the
flanks of the CDR3 sequence. Hence, the user is encouraged to remove a few
amino acids from the flanks of each CDR3 sequence. This can be done by
changing the control input from control$trim_flank_aa,
e.g.:
control$trim_flank_aa = 0
: no trimmingcontrol$trim_flank_aa = 3
: trim three amino acids from both flanks of the
CDR3 sequenceAn example of trimming CDR3 flanks and segmenting the core of the CDR3 sequence
into 4-mers is shown below. We now focus on five overlapping motifs found in
the core region of the CASSTTTGTGELFF
.
t <- 3
cdr3_trimmed <- substr(x = cdr3, start = t + 1, stop = nchar(cdr3) - t)
colnames(stringdist::qgrams(cdr3_trimmed, q = ks))
#> [1] "STTT" "TTTG" "TTGT" "TGTG" "GTGE"
A motif is considered enriched if the following (user-defined) criteria are satisfied:
control$local_min_o
: minimum motif frequency in s
control$local_min_ove
: minimum ratio of observed vs. expected (OvE)
relative motif frequency, with \(OvE=\dfrac{f_s}{n_s}/\dfrac{f_r}{n_r}\)
s
and r
s
and r
control$local_max_fdr
: maximum false discovery rate (FDR). Corrected
p-value, computed by a one-sided Fisher’s exact test (effectively a
hypergeometric test).For global clustering, ClustIRR employs one of three strategies.
The user can select each strategy with the control parameters: global_smart
and global_pairs
Strategy 1: if control$global_smart = FALSE
(default)
Here, ClustIRR quantifies the dissimilarity between pairs of
CDR3 sequences using Hamming distances. Two CDR3 sequences with Hamming
distance \(\leq x\) are considered globally similar, where \(x\) is the user-
defined threshold control$global_max_hdist
(default = 1).
With this, ClustIRR provides a very simple and intuitive heuristic for identifying globally similar CDR3s. But, this approach also has drawbacks:
Strategy 2: if control$global_smart = TRUE
In this case, ClustIRR does pairwise alignment of CDR3 sequences and computes a BLOSUM62 score \(B\) that is normalized by the alignment length \(l\) \(\rightarrow\) \(NB = B/l\).
Strategy 3: if control$global_pairs
is specified by the user
ClustIRR also provides a second input option (see red input
in ClustIRR workflow), which allows the user to provide a
data.frame containing globally similar CDR3 sequences computed by complementary
approaches (e.g., tcrdist). This input can be provided via the input
parameter control$global_pairs
. In this case, global clustering is not
performed by ClustIRR.
The main function in ClustIRR is cluster_irr
. This function
returns an S4 object of class clust_irr
. The object contains two sublists
(slots):
s
) and parametersWe will describe the inputs, outputs, and the algorithm of ClustIRR with the help of the following case studies.
In this example, we will insert the motif LEAR in the core regions of the
first 20 CDR3b sequences of repertoire s
. With this, we simulate enrichment
of this motif.
This motif is not enriched in repertoire r
, and ClustIRR
should be able to detect LEAR as enriched:
# insert motif LEAR
substr(x = s$CDR3b[1:20], start = 6, stop = 9) <- "LEAR"
… and then we perform clustering with ClustIRR:
o <- cluster_irr(s = s, r = r)
In the following table we see that ClustIRR has detected
enrichment of LEAR in s
compared to r
:
# extract local motifs
local_motifs <- get_clustirr_clust(o)$CDR3b$local$m
# display only passed motifs
knitr::kable(local_motifs[local_motifs$pass == TRUE, ], row.names = FALSE)
motif | f_s | n_s | f_r | n_r | k | ove | ove_ci_l95 | ove_ci_h95 | p_value | fdr | pass |
---|---|---|---|---|---|---|---|---|---|---|---|
LEAR | 20 | 2844 | 2 | 28286 | 4 | 100.1755 | 28.54331 | Inf | 0.0e+00 | 0.0000000 | TRUE |
LLEA | 5 | 2844 | 0 | 28286 | 4 | Inf | 12.13360 | Inf | 6.3e-06 | 0.0073939 | TRUE |
Interestingly, the motif LLEA is also enriched. This is probably because LLEA and LEAR share the substring LEA, hence, the enrichment of LLEA can be seen as a byproduct of the inserted motif LEAR.
This can also be seen from the lower frequency of LLEA (\(f_s\) = 5) in s
compared to LEAR (\(f_s\) = 20). For LEAR we see FDR \(\approx 10^{-15}\),
which is significantly smaller than the FDR \(\approx 10^{-2}\) observed for
LLEA. Finally, for LEAR we see OvE \(\approx 100\), whereas for LLEA’s
OvE \(=\infty\) (LLEA has \(f_r\) = 0, which results in a division by 0 when
calculating OvE).
In our data we had only one pair of globally similar CDR3 sequences, i.e. the CDR3 sequences CASSPLEARGYTF and CASRPLEARGYTF which differ by one amino acid at position 4. ClustIRR has identified this:
# display globally similar pairs
knitr::kable(get_clustirr_clust(o)$CDR3b$global, row.names = FALSE)
from_cdr3 | to_cdr3 | weight | cweight | nweight | ncweight | max_len |
---|---|---|---|---|---|---|
CASSSSPGDQEQFF | CASSSSPGDNEQFF | 1 | 1 | 1 | 1 | NA |
CASSPLEARGYTF | CASRPLEARGYTF | 1 | 1 | 1 | 1 | NA |
To interpret the ClustIRR output we can inspect the tables
of locally/globally similar CDR3s. Furthermore, we provide the functions
get_graph
, which generates an undirected graph (igraph
object), and plot_graph
for visualization of the graph.
Each vertex in the graph is a T-cell clone (row in the input data.frame for
repertoire s
), and we draw an edge between two vertices if they are
globally similar or if they share an enriched motif (locally similar).
Multiple edges for global and local similarity between two vertices are
possible.
Let’s visualize the graph output for this case study:
par(mai = c(0,0,0,0))
plot_graph(clust_irr = o)
The graph shows a cluster of T-cell clones (densely connected vertices). These are the 20 clones with CDR3 sequences in which we inserted the motif LEAR. All clones that have an enriched motif are connected in the graph, i.e. enriched motifs give rise to cliques. The remaining clones (about 480) are shown as singleton vertices.
We also see two vertices that are connected by an edge. These are the two clones that are globally similar.
We can plot an interactive graph with visNetwork.
plot_graph(clust_irr = o, as_visnet = TRUE)
To remove all vertices without edges from the graph, we can disable the option show_singletons.
plot_graph(clust_irr = o, as_visnet = TRUE, show_singletons = FALSE)
Between a pair of vertices, we draw an edge if: a) they are globally similar; or b) they share an enriched motif. We can have multiple edges between a pair of vertices (clones) for each chain: local, global, local and global.
In this case study, we assume that the data contains a clone with 10
T-cells (\(\approx\) 2% of the size of the initial repertoire). All T-cells
in the expanded clone have the same CDR3b sequence CATSRPDGLAQYF.
ClustIRR should detect most motifs at the core of
CATSRPDGLAQYF as enriched while also reporting that all cells within
s
have globally similar (in fact identical) CDR3b sequences.
Let’s insert a clone in data set s
:
# create a clone of 10 T-cells
clone <- data.frame(CDR3b = "CATSRPDGLAQYF", clone_size = 10)
# append the clone to the original repertoire 's'
s <- rbind(s, clone)
… and once again perform clustering with ClustIRR:
o <- cluster_irr(s = s,
r = r,
ks = 4,
cores = 1,
control = list(global_smart = FALSE,
trim_flank_aa = 3))
ClustIRR once again reports enrichment of LEAR, but also of many additional motifs that are part of the core of CATSRPDGLAQYF, such as DGLA, PDGL, RPDG, SRPG, etc.
# extract local motifs
local_motifs <- get_clustirr_clust(o)$CDR3b$local$m
# display only passed motifs
knitr::kable(local_motifs[local_motifs$pass == TRUE, ], row.names = FALSE)
motif | f_s | n_s | f_r | n_r | k | ove | ove_ci_l95 | ove_ci_h95 | p_value | fdr | pass |
---|---|---|---|---|---|---|---|---|---|---|---|
DGLA | 10 | 2884 | 4 | 28286 | 4 | 24.58735 | 8.373281 | Inf | 0.00e+00 | 0.0000149 | TRUE |
EARG | 5 | 2884 | 2 | 28286 | 4 | 24.54653 | 5.085508 | Inf | 1.21e-04 | 0.0353058 | TRUE |
EARN | 4 | 2884 | 0 | 28286 | 4 | Inf | 8.805146 | Inf | 7.31e-05 | 0.0244007 | TRUE |
LEAR | 20 | 2884 | 2 | 28286 | 4 | 98.77539 | 28.142075 | Inf | 0.00e+00 | 0.0000000 | TRUE |
LLEA | 5 | 2884 | 0 | 28286 | 4 | Inf | 11.965141 | Inf | 6.80e-06 | 0.0026306 | TRUE |
PDGL | 10 | 2884 | 1 | 28286 | 4 | 98.43442 | 17.154644 | Inf | 0.00e+00 | 0.0000003 | TRUE |
RPDG | 10 | 2884 | 1 | 28286 | 4 | 98.43442 | 17.154644 | Inf | 0.00e+00 | 0.0000003 | TRUE |
SRPD | 10 | 2884 | 1 | 28286 | 4 | 98.43442 | 17.154644 | Inf | 0.00e+00 | 0.0000003 | TRUE |
Once again, ClustIRR finds the same pair of globally similar CDR3 sequences. The CDR3 sequences CASSPLEARGYTF and CASRPLEARGYTF differ by one amino acid at position 4.
Let’s check how the global and local similarities are represented in the graph (see next section). Can you find the vertex that corresponds to the expanded clone?
Let’s plot the clustering results:
plot_graph(clust_irr = o, as_visnet = TRUE)
We see one connected component, which is identical to the one we saw in case study 1. Furthermore, we see a large vertex. This represents the clonal expansion, where the size of the vertex scales as the logarithm of the number of T-cells in the clone.
Clonally expanded cells are globally similar to each other. If the specific
clonal expansion is only found in s
but not in r
, then it is likely that
we will also see an enrichment of certain motifs from the core of the
corresponding CDR3 sequences. In summary, CDR3s of expanded clones are similar
in terms of the global sequences but may also be locally similar.
Single-cell technology allows us to sequence entire TCR repertoires, and to extract the sequences of both TCR chains: \(\beta\) and \(\alpha\).
ClustIRR can analyze such data. Clustering is performed separately using the CDR3 sequences of each chain.
Let’s create the input data. We create two repertoires:
S0
: 100 CDR3\(\beta\) and CDR3\(\alpha\) sequence pairs.S1
: 1,000 CDR3\(\beta\) and CDR3\(\alpha\) sequence pairs.Imagine that S0
and S1
are two TCR repertoires of a cancer patient,
sequenced before and after cancer therapy, respectively.
data("CDR3ab")
S0 <- data.frame(CDR3a = CDR3ab$CDR3a[3001:3100],
CDR3b = CDR3ab$CDR3b[3001:3100],
clone_size = c(50, 10, rep(1, times = 98)))
S1 <- data.frame(CDR3a = CDR3ab$CDR3a[5001:5200],
CDR3b = CDR3ab$CDR3b[5001:5200],
clone_size = c(20, rep(1, times = 199)))
# create a clone of 15 T-cells
clone <- data.frame(CDR3a = "CASSQPGTDHGYTF",
CDR3b = "CASSPQGREATGELFF",
clone_size = 15)
# append the clone to the original repertoire 'S0'
S0 <- rbind(S0, clone)
# insert motif LEAR into S0
substr(x = S0$CDR3b[1:20], start = 6, stop = 9) <- "LEAR"
# insert motif WWWW into S1
substr(x = S1$CDR3b[1], start = 5, stop = 8) <- "WWWW"
TCR repertoire S0
has
S0-1
: 50 T-cells (CDR3a: CASSFGPYGSQPQHF, CDR3b: CASSRDAGNTIYF)S0-2
: 15 T-cells (CDR3a: CASSQPGTDHGYTF, CDR3b:
CASSPQGREATGELFF)S0-3
: 10 T-cells (CDR3a: CATSRLRQGLNEKLFF, CDR3b: CASGGGLAYEQYF)TCR repertoire S1
has
S1-1
: 20 T-cells (CDR3a: CASSQPGTDHGYTF, CDR3b:
CASSPQGREATGELFF)We will perform two sets of analysis with ClustIRR.
First, we will inspect how the specificity structure of repertoire S1
(s
=S1
) is modulated compare to repertoire S0
(r
=S0
).
clust_irrs <- vector(mode = "list", length = 2)
names(clust_irrs) <- c("S1", "S0")
clust_irrs[[1]] <- cluster_irr(s = S1, r = S0,
control = list(global_smart = FALSE,
trim_flank_aa = 3))
Second, we will do the reverse, i.e. we will inspect the specificity structure
of repertoire S0
(s
=S0
) compared to S1
(r
=S1
).
clust_irrs[[2]] <- cluster_irr(s = S0, r = S1,
control = list(global_smart = FALSE,
trim_flank_aa = 3))
Let’s plot the joint graph:
# beta & alpha chain
plot_joint_graph(clust_irrs = clust_irrs, cores = 1, as_visnet = TRUE)
#> creating graphs: 1
#> creating graphs: 2
#> merging clust_irr index: 1/1
The clones of repertoire S0
and S1
are shown as red and yellow
vertices, respectively.
Repertoire S0
contains three expanded clones. Hence, we see three large
red vertices. Repertoire S1
contains only one expanded clone shown as
a large yellow vertex. The remaining vertices are small and likely contain a
single T-cell.
Within each repertoire, the edges are drawn as explained earlier.
Meanwhile, in the joint graph we also have edges between the vertices from
both repertoires. These will be drawn, if a pair of clones in S0
and S1
have globally similar or equal CDR3 sequences. In this particular example, the
vertices that represents the expanded clones S0-2
and S1-1
(large red
and yellow vertices) are connected by such an edge.
ClustIRR can be employed to study the specificity structure of:
To carry out this analysis, the user must appropriately configure the inputs. Here we demonstrate this point. The rest of the clustering analysis proceeds similarly as outlined in case studies 1-3.
To analyze \(\alpha\beta\) chain TCR repertoires, the input data sets s
and r
are required to have one or both (e.g., if we have paired data as explained in
case study 3) of the columns: CDR3a
and CDR3b
.
For the analysis of \(\gamma\delta\) chain TCR repertoires the columns CDR3g
and CDR3d
have to be specified. For the analysis of heavy(H)/light(L)
chain BCR repertoires ClustIRR uses the two columns CDR3h and
CDR3l.
Dummy example, where we take the CDR3ab
data set as input and simply rename
the columns:
data("CDR3ab")
# gamma/delta chain TCR data -> notice CDR3g and CDR3d columns of 's' and 'r'
s <- data.frame(CDR3g = CDR3ab$CDR3a[4501:5000],
CDR3d = CDR3ab$CDR3b[4501:5000])
r <- data.frame(CDR3g = CDR3ab$CDR3a[5001:10000],
CDR3d = CDR3ab$CDR3b[5001:10000])
data("CDR3ab")
# heavy/light chain BCR data -> notice CDR3h and CDR3l columns of 's' and 'r'
s <- data.frame(CDR3h = CDR3ab$CDR3a[4501:5000],
CDR3l = CDR3ab$CDR3b[4501:5000])
r <- data.frame(CDR3h = CDR3ab$CDR3a[5001:10000],
CDR3l = CDR3ab$CDR3b[5001:10000])
The rest of the analysis proceeds as usual, i.e., by calling the function
cluster_irr
.
Imagine that we have a two patients, P1 and P2. From each patient we have two IRRs, one before (A) and one after (B) treatment.
data("CDR3ab")
P1_A <- data.frame(CDR3a = CDR3ab$CDR3a[3001:3100],
CDR3b = CDR3ab$CDR3b[3001:3100],
clone_size = rpois(n = 100, lambda = 1)+1)
P1_B <- data.frame(CDR3a = CDR3ab$CDR3a[c(3100, 3101:3200)],
CDR3b = CDR3ab$CDR3b[c(3100, 3101:3200)],
clone_size = rpois(n = 101, lambda = 1)+1)
# clone_size column -> poisson distributed
P2_A <- data.frame(CDR3a = CDR3ab$CDR3a[c(3100, 4001:4100)],
CDR3b = CDR3ab$CDR3b[c(3100, 4001:4100)])
P2_B <- data.frame(CDR3a = CDR3ab$CDR3a[c(3100, 4001:4100)],
CDR3b = CDR3ab$CDR3b[c(3100, 4001:4100)])
# no clone_size column -> default clone_size = 1
We can analyze the specificity structure of IRR A and B by performing
clustering with ClustIRR. Here we perform two analyses:
A vs B and B vs. A, which allows us to find enriched motifs from either
direction. The results are stored in a list of size 4. When r
is set to
NULL
, only global clustering is performed.
clust_irrs <- vector(mode = "list", length = 4)
names(clust_irrs) <- c("P1_A", "P1_B", "P2_A", "P2_B")
clust_irrs[[1]] <- cluster_irr(s = P1_A, r = NULL)
#> missing input r, global clustering mode only
clust_irrs[[2]] <- cluster_irr(s = P1_B, r = NULL)
#> missing input r, global clustering mode only
clust_irrs[[3]] <- cluster_irr(s = P2_A, r = NULL)
#> missing input r, global clustering mode only
clust_irrs[[4]] <- cluster_irr(s = P2_B, r = NULL)
#> missing input r, global clustering mode only
ClustIRR provides the unique opportunity to merge the four graphs (in fact: as many ClustIRR outputs as we want) and to visualize them.
# beta & alpha chain
plot_joint_graph(clust_irrs = clust_irrs,
as_visnet = TRUE,
show_singletons = T)
#> creating graphs: 1
#> creating graphs: 2
#> creating graphs: 3
#> creating graphs: 4
#> merging clust_irr index: 1/3
#> merging clust_irr index: 2/3
#> merging clust_irr index: 3/3
Clones are color-coded according to the four input repertoires:
P1_A
= redP1_B
= yellowP2_A
= greenP2_B
= blueThe clones present in both samples from the same patient are shown as connected vertices.
Clones with larger clone size appear as bigger vertices than clones with smaller clone size.
Similarities between clones from different repertoires are drawn as edges
between different colored vertices. In this case, we can see many connections
between clones from P2_A
and P2_B
, and only one connection P1_A
and
P1_B
, which also connects to clones from P2_A
and P2_B
.
Within each repertoire, edges are drawn as explained earlier (not the case in this particular graph).
utils::sessionInfo()
#> R version 4.4.0 beta (2024-04-15 r86425)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 22.04.4 LTS
#>
#> Matrix products: default
#> BLAS: /home/biocbuild/bbs-3.19-bioc/R/lib/libRblas.so
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.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: America/New_York
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] visNetwork_2.1.2 knitr_1.46 ClustIRR_1.2.0 BiocStyle_2.32.0
#>
#> loaded via a namespace (and not attached):
#> [1] jsonlite_1.8.8 highr_0.10 compiler_4.4.0
#> [4] BiocManager_1.30.22 crayon_1.5.2 tinytex_0.50
#> [7] Rcpp_1.0.12 magick_2.8.3 Biostrings_2.72.0
#> [10] parallel_4.4.0 jquerylib_0.1.4 IRanges_2.38.0
#> [13] yaml_2.3.8 fastmap_1.1.1 blaster_1.0.7
#> [16] R6_2.5.1 XVector_0.44.0 igraph_2.0.3
#> [19] GenomeInfoDb_1.40.0 BiocGenerics_0.50.0 htmlwidgets_1.6.4
#> [22] bookdown_0.39 GenomeInfoDbData_1.2.12 bslib_0.7.0
#> [25] rlang_1.1.3 cachem_1.0.8 pwalign_1.0.0
#> [28] xfun_0.43 sass_0.4.9 cli_3.6.2
#> [31] magrittr_2.0.3 zlibbioc_1.50.0 stringdist_0.9.12
#> [34] digest_0.6.35 lifecycle_1.0.4 S4Vectors_0.42.0
#> [37] glue_1.7.0 evaluate_0.23 stats4_4.4.0
#> [40] rmarkdown_2.26 httr_1.4.7 tools_4.4.0
#> [43] pkgconfig_2.0.3 htmltools_0.5.8.1 UCSC.utils_1.0.0