Contents

Congratulations on your installation of CellaRepertorium. This package contains methods for manipulating, clustering, pairing, testing and conducting multimodal analysis single cell RepSeq data, especially as generated by 10X Genomics Chromium Immune Profiling.

1 Ethos and data structure

The fundamental unit this package operates on is the contig, which is a section of contiguously stitched reads from a single cell. Each contig belongs to one (and only one) cell, however, cells may generate multiple contigs.

Contigs belong to cells, and can also belong to a cluster. A ContigCellDB() object tracks these two types of membership by using a sequence of three data.frames (dplyr::tibble(), actually). ContigCellDB() also tracks columns (the primary keys) that uniquely identify each row in each of these tables. The contig_tbl is the tibble containing contigs, the cell_tbl contains the cells, and the cluster_tbl contains the clusters.

The contig_pk, cell_pk and cluster_pk specify the columns that identify a contig, cell and cluster, respectively. These will serve as foreign keys that link the three tables together.

2 Manipulation

We’ll start in media res with an example of minimially-processed annotated contigs from Cellranger. For details on how to import your own CellRanger data, as well QC steps that could be performed, see vignette('mouse_tcell_qc').

library(CellaRepertorium)
library(dplyr)
data("contigs_qc")
cdb = ContigCellDB_10XVDJ(contigs_qc, 
                          contig_pk = c('barcode', 'pop', 'sample', 'contig_id'), 
                          cell_pk = c('barcode', 'pop', 'sample'))

This constructs a ContigCellDB object, specifying that the columns barcode, pop, sample, and contig_id unique identify a contig, so are its primary keys, and that columns barcode, pop, sample are the cell primary keys.

We can manipulate the contig_tbl with the $ operator.

cdb$contig_tbl$cdr_nt_len = nchar(cdb$contig_tbl$cdr3_nt)

Or with the mutate_cdb function, which saves a few keystrokes.

suppressPackageStartupMessages(library(Biostrings))
cdb = cdb %>% mutate_cdb(cdr3_g_content = alphabetFrequency(DNAStringSet(cdr3_nt))[,'G'], tbl = 'contig_tbl')

head(cdb$contig_tbl, n = 4) %>% 
    select(contig_id, cdr3_nt, cdr_nt_len, cdr3_g_content)
#> # A tibble: 4 × 4
#>   contig_id                   cdr3_nt                  cdr_nt_len cdr3_g_content
#>   <chr>                       <chr>                         <int>          <int>
#> 1 AAAGTAGTCGCGCCAA-1_contig_1 TGTGCCAGCAGTCCGACAGACTA…         36              8
#> 2 AAAGTAGTCGCGCCAA-1_contig_2 TGTGCCTGGAGTCCCGGGGACAA…         39             12
#> 3 AAAGTAGTCGCGCCAA-1_contig_4 TGTGCTATAGAGGCAGGCAATAC…         39              9
#> 4 AACCATGCATTTGCCC-1_contig_3 TGTGCTGTGAGCGCATACCAGGG…         42             14

Other functionality, some of which is depicted in vignette('mouse_tcell_qc') and vignette('cdr3_clustering') includes:

3 Clustering

We provide an R port of the fast biostring clustering algorithm CD-HIT (Fu et al. 2012).

aa80 = cdhit_ccdb(cdb, 'cdr3', type = 'AA', cluster_pk = 'aa80', 
                  identity = .8, min_length = 5)
aa80 = fine_clustering(aa80, sequence_key = 'cdr3', type = 'AA')
#> Calculating intradistances on 997 clusters.
#> Summarizing

This partitions sequences into sets with >80% mutual similarity in the amino acid sequence, adds some additional information about the clustering, and returns it as a ContigCellDB object named aa80. The primary key for the clusters is aa80.

head(aa80$cluster_tbl)
#> # A tibble: 6 × 3
#>    aa80 avg_distance n_cluster
#>   <dbl>        <dbl>     <int>
#> 1     1            0         1
#> 2     2            0         1
#> 3     3            0         2
#> 4     4            0         1
#> 5     5            0         1
#> 6     6            0         1
head(aa80$contig_tbl) %>% select(contig_id, aa80, is_medoid, `d(medoid)`)
#> # A tibble: 6 × 4
#>   contig_id                    aa80 is_medoid `d(medoid)`
#>   <chr>                       <dbl> <lgl>           <dbl>
#> 1 ATCTACTCAGTATGCT-1_contig_3     1 TRUE                0
#> 2 ACTGTCCTCAATCACG-1_contig_3     2 TRUE                0
#> 3 CACCTTGTCCAATGGT-1_contig_2     3 TRUE                0
#> 4 CACCTTGTCCAATGGT-1_contig_2     3 FALSE               0
#> 5 CGGACGTGTTCATGGT-1_contig_1     4 TRUE                0
#> 6 CTGCTGTTCCCTAATT-1_contig_4     5 TRUE                0

The cluster_tbl lists the 997 80% identity groups found, including the number of contigs in the cluster, and the average distance between elements in the group. In the contig_tbl, there are two columns specifying if the contig is_medoid, that is, is the most representative element of the set and the distance to the medoid element d(medoid).

Other functionality to operate on clustering include:

4 Pairing

One of the main benefits of single cell repertoire sequencing is the ability to recover both light and heavy chains, or alpha and beta chains of B cells and T cells. Pairing is a property of the cell_tbl. We provide a number of tools to analyze and visualize the pairing.

library(ggplot2)

paired_chain = enumerate_pairing(cdb, chain_recode_fun = 'guess')
#> Warning: `fct_explicit_na()` was deprecated in forcats 1.0.0.
#> ℹ Please use `fct_na_value_to_level()` instead.
#> ℹ The deprecated feature was likely used in the CellaRepertorium package.
#>   Please report the issue at
#>   <https://github.com/amcdavid/CellaRepertorium/issues>.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.

ggplot(paired_chain, aes(x = interaction(sample, pop), fill = pairing)) + 
    geom_bar() + facet_wrap(~canonical, scale = 'free_x') + 
    coord_flip() + theme_minimal()

We first determine how often cells were paired, and how often non-canonical multi-alpha or multi-beta cells are found.
T cells with two alpha chains, and to a lessor extent, two beta chains are an established phenomena (Padovan et al. 1993, 1995; He et al. 2002). However, if the rate of these so-called dual TCR cells is too high, then multiplets or excess ambient RNA may be suspected. (B plasma cells seem to be particularly sticky, and are laden with immunoglobulin RNA).

4.1 Enumerating clonotypically expanded pairs

Besides this high-level description of the rate and characteristics of the pairing, we want to discover pairs that occur repeatedly. For that, we can use the pairing_tables function.

First we copy some info into the cluster_tbl from the medoid contig:

aa80 = canonicalize_cluster(aa80, representative = 'cdr3', 
                            contig_fields = c('cdr3', 'cdr3_nt', 'chain', 'v_gene', 'd_gene', 'j_gene'))
#> Filtering `contig_tbl` by `is_medoid`, override by setting `contig_filter_args == TRUE`
aa80$cluster_pk = 'representative'

The representative just gives the clusters a more useful unique name (the CDR3 animo acid sequence). The other information would be helpful with visualizing and understanding the results of the pairing.

Next we provide an ordering for each contig in a cell:

aa80 = rank_chain_ccdb(aa80)

In this case the contigs will be picked by the beta/alpha chain of the cluster. Other options are possible, for instance the prevalence of the cluster with rank_prevalence_ccdb, which would allow detection to see expanded, dual-TCR pairings (alpha-alpha or beta-beta).

Finally, we generate the pairings.

pairing_list = pairing_tables(aa80, table_order = 2, orphan_level = 1, min_expansion = 3, cluster_keys = c('cdr3', 'representative', 'chain', 'v_gene', 'j_gene', 'avg_distance'))

By default, this is subset to a list suitable for plotting in a heatmap-like format, so includes only expanded pairings. You can get all pairings by setting min_expansion = 1, and force inclusion or exclusion of particular clusters with cluster_whitelist or cluster_blacklist.

pairs_plt = ggplot(pairing_list$cell_tbl, aes(x = cluster_idx.1_fct, y = cluster_idx.2_fct)) + 
  geom_jitter(aes(color = sample, shape = pop), width = .2, height = .2) + 
  theme_minimal() + xlab('TRB') + ylab('TRA') + 
  theme(axis.text.x = element_text(angle = 45))

pairs_plt = map_axis_labels(pairs_plt, pairing_list$idx1_tbl, pairing_list$idx2_tbl, aes_label  = 'chain')
pairs_plt

5 Testing

If the experiment that generated the data was a designed experiment, it might be of interest to test clusters for differential abundance. We implement ordinary and mixed-effect logistic and binomial tests. See cluster_logistic_test for details and vignette('cdr3_clustering') for an example.

We also implement a permutation test which may be suitable for testing for various phylogenetic properties of clonotypes, such as the clonotype diversity or polarization. Cluster assignments are permutated by cells, possibly conditioning on less granular covariates. See cluster_permute_test and vignette('cdr3_clustering').

6 Multimodal Analysis

ContigCellDB objects can be included as a field on the colData of a SingleCellExperiment. This permits various multimodal analyses, while maintaining the correspondence between the cells in the SinglCellExperiment and the cells in the ContigCellDB. See vignette('repertoire_and_expression').

7 Acknowledgments

Development of CellaRepertorium was funded in part by UL1 TR002001 (PI Bennet/Zand) pilot to Andrew McDavid.

8 Colophone

sessionInfo()
#> R version 4.3.1 (2023-06-16)
#> Platform: x86_64-pc-linux-gnu (64-bit)
#> Running under: Ubuntu 22.04.3 LTS
#> 
#> Matrix products: default
#> BLAS:   /home/biocbuild/bbs-3.18-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_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] Biostrings_2.70.0       GenomeInfoDb_1.38.0     IRanges_2.36.0         
#>  [4] S4Vectors_0.40.0        BiocGenerics_0.48.0     ggdendro_0.1.23        
#>  [7] XVector_0.42.0          purrr_1.0.2             stringr_1.5.0          
#> [10] tidyr_1.3.0             readr_2.1.4             ggplot2_3.4.4          
#> [13] dplyr_1.1.3             CellaRepertorium_1.12.0 BiocStyle_2.30.0       
#> 
#> loaded via a namespace (and not attached):
#>  [1] tidyselect_1.2.0        farver_2.1.1            bitops_1.0-7           
#>  [4] fastmap_1.1.1           RCurl_1.98-1.12         digest_0.6.33          
#>  [7] lifecycle_1.0.3         magrittr_2.0.3          compiler_4.3.1         
#> [10] progress_1.2.2          rlang_1.1.1             sass_0.4.7             
#> [13] tools_4.3.1             utf8_1.2.4              yaml_2.3.7             
#> [16] knitr_1.44              prettyunits_1.2.0       labeling_0.4.3         
#> [19] plyr_1.8.9              RColorBrewer_1.1-3      withr_2.5.1            
#> [22] grid_4.3.1              fansi_1.0.5             colorspace_2.1-0       
#> [25] future_1.33.0           globals_0.16.2          scales_1.2.1           
#> [28] MASS_7.3-60             cli_3.6.1               rmarkdown_2.25         
#> [31] crayon_1.5.2            generics_0.1.3          reshape2_1.4.4         
#> [34] tzdb_0.4.0              minqa_1.2.6             cachem_1.0.8           
#> [37] zlibbioc_1.48.0         splines_4.3.1           parallel_4.3.1         
#> [40] BiocManager_1.30.22     vctrs_0.6.4             boot_1.3-28.1          
#> [43] Matrix_1.6-1.1          jsonlite_1.8.7          bookdown_0.36          
#> [46] hms_1.1.3               listenv_0.9.0           magick_2.8.1           
#> [49] jquerylib_0.1.4         glue_1.6.2              parallelly_1.36.0      
#> [52] nloptr_2.0.3            codetools_0.2-19        cowplot_1.1.1          
#> [55] stringi_1.7.12          gtable_0.3.4            lme4_1.1-34            
#> [58] broom.mixed_0.2.9.4     munsell_0.5.0           tibble_3.2.1           
#> [61] pillar_1.9.0            furrr_0.3.1             htmltools_0.5.6.1      
#> [64] GenomeInfoDbData_1.2.11 R6_2.5.1                evaluate_0.22          
#> [67] lattice_0.22-5          backports_1.4.1         broom_1.0.5            
#> [70] bslib_0.5.1             Rcpp_1.0.11             nlme_3.1-163           
#> [73] xfun_0.40               forcats_1.0.0           pkgconfig_2.0.3

References

Fu, Limin, Beifang Niu, Zhengwei Zhu, Sitao Wu, and Weizhong Li. 2012. “CD-HIT: accelerated for clustering the next-generation sequencing data.” Bioinformatics (Oxford, England) 28 (23): 3150–2. https://doi.org/10.1093/bioinformatics/bts565.

He, Xin, Charles A. Janeway, Matthew Levine, Eve Robinson, Paula Preston-Hurlburt, Christophe Viret, and Kim Bottomly. 2002. “Dual receptor T cells extend the immune repertoire for foreign antigens.” Nature Immunology. https://doi.org/10.1038/ni751.

Padovan, Elisabetta, Giulia Casorati, Paolo Dellabona, Stefan Meyer, Manfred Brockhaus, and Antonio Lanzavecchia. 1993. “Expression of two T cell receptor \(\alpha\) chains: Dual receptor T cells.” Science. https://doi.org/10.1126/science.8211163.

Padovan, Elisabetta, Claudia Giachino, Marina Cella, Salvatore Valitutti, Oreste Acuto, and Antonio Lanzavecchia. 1995. “Normal T lymphocytes can express two different T cell receptor \(\beta\) chains: Implications for the mechanism of allelic exclusion.” Journal of Experimental Medicine. https://doi.org/10.1084/jem.181.4.1587.