# CiteFuse: getting started

Yingxin Lin1 and Hani Jieun Kim1,2,3

1School of Mathematics and Statistics, The University of Sydney, Australia
2Charles Perkins Centre, The University of Sydney, Australia
3Computational Systems Biology Group, Children’s Medical Research Institute, Faculty of Medicine and Health, The University of Sydney, Australia

#### 25 April 2023

# 1 Introduction

`CiteFuse` is a computational framework that implements a suite of methods and tools for CITE-seq data from pre-processing through to integrative analytics. This includes doublet detection, network-based modality integration, cell type clustering, differential RNA and protein expression analysis, ADT evaluation, ligand-receptor interaction analysis, and interactive web-based visualisation of the analyses. This vignette demonstrates the usage of `CiteFuse` on a subset data of CITE-seq data from human PBMCs as an example (Mimitou et al., 2019).

First, install `CiteFuse` using `BiocManager`.

    if (!requireNamespace("BiocManager", quietly = TRUE)) {
     install.packages("BiocManager")
    }
    BiocManager::install("CiteFuse")

    library(CiteFuse)
    library(scater)
    library(SingleCellExperiment)
    library(DT)

    data("CITEseq_example", package = "CiteFuse")
    names(CITEseq_example)
    #> [1] "RNA" "ADT" "HTO"
    lapply(CITEseq_example, dim)
    #> $RNA
    #> [1] 19521   500
    #>
    #> $ADT
    #> [1]  49 500
    #>
    #> $HTO
    #> [1]   4 500

Here, we start from a list of three matrices of unique molecular identifier (UMI), antibody derived tags (ADT) and hashtag oligonucleotide (HTO) count, which have common cell names. There are 500 cells in our subsetted dataset. And characteristically of CITE-seq data, the matrices are matched, meaning that for any given cell we know the expression level of their RNA transcripts (genome-wide) and its corresponding cell surface protein expression. The `preprocessing` function will utilise the three matrices and its common cell names to create a `SingleCellExperiment` object, which stores RNA data in an `assay` and `ADT` and `HTO` data within in the `altExp` slot.

    sce_citeseq <- preprocessing(CITEseq_example)
    sce_citeseq
    #> class: SingleCellExperiment
    #> dim: 19521 500
    #> metadata(0):
    #> assays(1): counts
    #> rownames(19521): hg19_AL627309.1 hg19_AL669831.5 ... hg19_MT-ND6
    #>   hg19_MT-CYB
    #> rowData names(0):
    #> colnames(500): AAGCCGCGTTGTCTTT GATCGCGGTTATCGGT ... TTGGCAACACTAGTAC
    #>   GCTGCGAGTTGTGGCC
    #> colData names(0):
    #> reducedDimNames(0):
    #> mainExpName: NULL
    #> altExpNames(2): ADT HTO

# 2 Detecting both cross- and within-sample doublets using `CiteFuse`

## 2.1 HTO Normalisation and Visualisation

The function `normaliseExprs` is used to scale the alternative expression. Here, we used it to perform log-transformation of the `HTO` count, by setting `transform = "log"`.

    sce_citeseq <- normaliseExprs(sce = sce_citeseq,
                                  altExp_name = "HTO",
                                  transform = "log")

Then we can perform dimension reduction on the `HTO` count by using `runTSNE` or `runUMAP`, then use `visualiseDim` function to visualise the reduced dimension plot. Our CITE-seq dataset contain data from four samples that were pooled before sequencing. The samples were multiplexed through cell hashing (Stoekius et al., 2018). The four clusters observed on reduced dimension plots equate to the four different samples.

    sce_citeseq <- scater::runTSNE(sce_citeseq,
                                   altexp = "HTO",
                                   name = "TSNE_HTO",
                                   pca = TRUE)

    visualiseDim(sce_citeseq,
                 dimNames = "TSNE_HTO") + labs(title = "tSNE (HTO)")

    sce_citeseq <- scater::runUMAP(sce_citeseq,
                                   altexp = "HTO",
                                   name = "UMAP_HTO")

    visualiseDim(sce_citeseq,
                 dimNames = "UMAP_HTO") + labs(title = "UMAP (HTO)")

## 2.2 Doublet identification step 1: cross-sample doublet detection

An important step in single cell data analysis is the removal of doublets. Doublets form as a result of co-encapsulation of cells within a droplet, leading to a hybrid transcriptome from two or more cells. In CiteFuse, we implement a step-wise doublet detection approach to remove doublets. We first identify the cross-sample doublets via the `crossSampleDoublets` function.

    sce_citeseq <- crossSampleDoublets(sce_citeseq)
    #> number of iterations= 20
    #> number of iterations= 24
    #> number of iterations= 46
    #> number of iterations= 50

The results of the cross sample doublets are then saved in `colData` as `doubletClassify_between_label` and `doubletClassify_between_class`.

    table(sce_citeseq$doubletClassify_between_label)
    #>
    #>                 1                 2                 3                 4
    #>               115               121                92               129
    #> doublet/multiplet
    #>                43
    table(sce_citeseq$doubletClassify_between_class)
    #>
    #>           Singlet doublet/multiplet
    #>               457                43

We can then highlight the cross-sample doublets in our tSNE plot of HTO count.

    visualiseDim(sce_citeseq,
                 dimNames = "TSNE_HTO",
                 colour_by = "doubletClassify_between_label")

Furthermore, `plotHTO` function allows us to plot the pairwise scatter HTO count. Any cells that show co-expression of orthologocal HTOs (red) are considered as doublets.

    plotHTO(sce_citeseq, 1:4)

## 2.3 Doublet identification step 1: within-sample doublet detection

We then identify the within-sample doublets via the `withinSampleDoublets` function.

    sce_citeseq <- withinSampleDoublets(sce_citeseq,
                                        minPts = 10)

The results of the cross sample doublets are then saved in the `colData` as `doubletClassify_within_label` and `doubletClassify_within_class`.

    table(sce_citeseq$doubletClassify_within_label)
    #>
    #>  Doublets(Within)_1  Doublets(Within)_2  Doublets(Within)_3  Doublets(Within)_4
    #>                   3                   7                   4                   6
    #> NotDoublets(Within)
    #>                 480
    table(sce_citeseq$doubletClassify_within_class)
    #>
    #> Doublet Singlet
    #>      20     480

Again, we can visualise the within-sample doublets in our tSNE plot.

    visualiseDim(sce_citeseq,
                 dimNames = "TSNE_HTO",
                 colour_by = "doubletClassify_within_label")

Finally, we can filter out the doublet cells (both within and between batches) for the downstream analysis.

    sce_citeseq <- sce_citeseq[, sce_citeseq$doubletClassify_within_class == "Singlet" & sce_citeseq$doubletClassify_between_class == "Singlet"]
    sce_citeseq
    #> class: SingleCellExperiment
    #> dim: 19521 437
    #> metadata(3): doubletClassify_between_threshold
    #>   doubletClassify_between_resultsMat doubletClassify_within_resultsMat
    #> assays(1): counts
    #> rownames(19521): hg19_AL627309.1 hg19_AL669831.5 ... hg19_MT-ND6
    #>   hg19_MT-CYB
    #> rowData names(0):
    #> colnames(437): GATCGCGGTTATCGGT GGCTGGTAGAGGTTAT ... TTGGCAACACTAGTAC
    #>   GCTGCGAGTTGTGGCC
    #> colData names(5): doubletClassify_between_label
    #>   doubletClassify_between_class nUMI doubletClassify_within_label
    #>   doubletClassify_within_class
    #> reducedDimNames(2): TSNE_HTO UMAP_HTO
    #> mainExpName: NULL
    #> altExpNames(2): ADT HTO

# 3 Clustering

## 3.1 Performing SNF

The first step of analysis is to integrate the RNA and ADT matrix. We use a popular integration algorithm called similarity network fusion (SNF) to integrate the multiomic data.

    sce_citeseq <- scater::logNormCounts(sce_citeseq)
    sce_citeseq <- normaliseExprs(sce_citeseq, altExp_name = "ADT", transform = "log")
    system.time(sce_citeseq <- CiteFuse(sce_citeseq))
    #> Calculating affinity matrix
    #> Performing SNF
    #>    user  system elapsed
    #>    3.67    0.08    3.75

We now proceed with the fused matrix, which is stored as `SNF_W` in our `sce_citeseq` object.

## 3.2 Performing spectral clustering

CiteFuse implements two different clustering algorithms on the fused matrix, spectral clustering and Louvain clustering. First, we perform spectral clustering with sufficient numbers of `K` and use the eigen values to determine the optimal number of clusters.

    SNF_W_clust <- spectralClustering(metadata(sce_citeseq)[["SNF_W"]], K = 20)
    #> Computing Spectral Clustering
    plot(SNF_W_clust$eigen_values)

    which.max(abs(diff(SNF_W_clust$eigen_values)))
    #> [1] 5

Using the optimal cluster number defined from the previous step, we can now use the `spectralClutering` function to cluster the single cells by specifying the number of clusters in `K`. The function takes a cell-to-cell similarity matrix as an input. We have already created the fused similarity matrix from `CiteFuse`. Since the `CiteFuse` function creates and stores the similarity matries from ADT and RNA expression, as well the fused matrix, we can use these two to compare the clustering outcomes by data modality.

    SNF_W_clust <- spectralClustering(metadata(sce_citeseq)[["SNF_W"]], K = 5)
    #> Computing Spectral Clustering
    sce_citeseq$SNF_W_clust <- as.factor(SNF_W_clust$labels)

    SNF_W1_clust <- spectralClustering(metadata(sce_citeseq)[["ADT_W"]], K = 5)
    #> Computing Spectral Clustering
    sce_citeseq$ADT_clust <- as.factor(SNF_W1_clust$labels)

    SNF_W2_clust <- spectralClustering(metadata(sce_citeseq)[["RNA_W"]], K = 5)
    #> Computing Spectral Clustering
    sce_citeseq$RNA_clust <- as.factor(SNF_W2_clust$labels)

## 3.3 Visualisation

The outcome of the clustering can be easily visualised on a reduced dimensions plot by highlighting the points by cluster label.

    sce_citeseq <- reducedDimSNF(sce_citeseq,
                                 method = "tSNE",
                                 dimNames = "tSNE_joint")

    g1 <- visualiseDim(sce_citeseq, dimNames = "tSNE_joint", colour_by = "SNF_W_clust") +
      labs(title = "tSNE (SNF clustering)")
    g2 <- visualiseDim(sce_citeseq, dimNames = "tSNE_joint",  colour_by = "ADT_clust") +
      labs(title = "tSNE (ADT clustering)")
    g3 <- visualiseDim(sce_citeseq, dimNames = "tSNE_joint",  colour_by = "RNA_clust") +
      labs(title = "tSNE (RNA clustering)")

    library(gridExtra)
    grid.arrange(g3, g2, g1, ncol = 2)

The expression of genes and proteins can be visualised by changing the `colour_by` parameter to assess the clusters. As an example, we highlight the plot by the RNA and ADT expression level of CD8.

    g1 <- visualiseDim(sce_citeseq, dimNames = "tSNE_joint",
                       colour_by = "hg19_CD8A",
                       data_from = "assay",
                       assay_name = "logcounts") +
      labs(title = "tSNE: hg19_CD8A (RNA expression)")
    g2 <- visualiseDim(sce_citeseq,dimNames = "tSNE_joint",
                       colour_by = "CD8",
                       data_from = "altExp",
                       altExp_assay_name = "logcounts") +
      labs(title = "tSNE: CD8 (ADT expression)")

    grid.arrange(g1, g2, ncol = 2)

## 3.4 Louvain clustering

As well as spectral clustering, CiteFuse can implement Louvain clustering if users wish to use another clustering method. We use the `igraph` package, and any community detection algorithms available in their package can be selected by changing the `method` parameter.

    SNF_W_louvain <- igraphClustering(sce_citeseq, method = "louvain")
    table(SNF_W_louvain)
    #> SNF_W_louvain
    #>   1   2   3   4   5   6   7
    #>  88 137  65  32  51  28  36

    sce_citeseq$SNF_W_louvain <- as.factor(SNF_W_louvain)

    visualiseDim(sce_citeseq, dimNames = "tSNE_joint", colour_by = "SNF_W_louvain") +
      labs(title = "tSNE (SNF louvain clustering)")

    visualiseKNN(sce_citeseq, colour_by = "SNF_W_louvain")

# 4 Differential Expression Analysis

## 4.1 Exploration of feature expression

CiteFuse has a wide range of visualisation tools to facilitate exploratory analysis of CITE-seq data. The `visualiseExprs` function is an easy-to-use function to generate boxplots, violinplots, jitter plots, density plots, and pairwise scatter/density plots of genes and proteins expressed in the data. The plots can be grouped by using the cluster labels stored in the `sce_citeseq` object.

    visualiseExprs(sce_citeseq,
                   plot = "boxplot",
                   group_by = "SNF_W_louvain",
                   feature_subset = c("hg19_CD2", "hg19_CD4", "hg19_CD8A", "hg19_CD19"))

    visualiseExprs(sce_citeseq,
                   plot = "violin",
                   group_by = "SNF_W_louvain",
                   feature_subset = c("hg19_CD2", "hg19_CD4", "hg19_CD8A", "hg19_CD19"))

    visualiseExprs(sce_citeseq,
                   plot = "jitter",
                   group_by = "SNF_W_louvain",
                   feature_subset = c("hg19_CD2", "hg19_CD4", "hg19_CD8A", "hg19_CD19"))

    visualiseExprs(sce_citeseq,
                   plot = "density",
                   group_by = "SNF_W_louvain",
                   feature_subset = c("hg19_CD2", "hg19_CD4", "hg19_CD8A", "hg19_CD19"))

    visualiseExprs(sce_citeseq,
                   altExp_name = "ADT",
                   group_by = "SNF_W_louvain",
                   plot = "violin", n = 5)

    visualiseExprs(sce_citeseq, altExp_name = "ADT",
                   plot = "jitter",
                   group_by = "SNF_W_louvain",
                   feature_subset = c("CD2", "CD8", "CD4", "CD19"))

    visualiseExprs(sce_citeseq, altExp_name = "ADT",
                   plot = "density",
                   group_by = "SNF_W_louvain",
                   feature_subset = c("CD2", "CD8", "CD4", "CD19"))

    visualiseExprs(sce_citeseq, altExp_name = "ADT",
                   plot = "pairwise",
                   feature_subset = c("CD4", "CD8"))
    #> number of iterations= 14
    #> number of iterations= 20

    visualiseExprs(sce_citeseq, altExp_name = "ADT",
                   plot = "pairwise",
                   feature_subset = c("CD45RA", "CD4", "CD8"),
                   threshold = rep(4, 3))

## 4.2 Perform DE Analysis with Wilcoxon Rank Sum test

CiteFuse also calculates differentially expressed (DE) genes through the `DEgenes` function. The cluster grouping to use must be specified in the `group` parameter. If `altExp_name` is not specified, RNA expression will be used as the default expression matrix.

Results form the DE analysis is stored in `sce_citeseq` as `DE_res_RNA_filter` and `DE_res_ADT_filter` for RNA and ADT expression, respectively.

### 4.2.1 For RNA expression

    # DE will be performed for RNA if altExp_name = "none"
    sce_citeseq <- DEgenes(sce_citeseq,
                           altExp_name = "none",
                           group = sce_citeseq$SNF_W_louvain,
                           return_all = TRUE,
                           exprs_pct = 0.5)

    sce_citeseq <- selectDEgenes(sce_citeseq,
                                 altExp_name = "none")
    datatable(format(do.call(rbind, metadata(sce_citeseq)[["DE_res_RNA_filter"]]),
                     digits = 2))

### 4.2.2 For ADT count

    sce_citeseq <- DEgenes(sce_citeseq,
                           altExp_name = "ADT",
                           group = sce_citeseq$SNF_W_louvain,
                           return_all = TRUE,
                           exprs_pct = 0.5)

    sce_citeseq <- selectDEgenes(sce_citeseq,
                                 altExp_name = "ADT")
    datatable(format(do.call(rbind, metadata(sce_citeseq)[["DE_res_ADT_filter"]]),
                     digits = 2))

## 4.3 Visualising DE Results

The DE genes can be visualised with the `DEbubblePlot` and `DEcomparisonPlot`. In each case, the gene names must first be extracted from the DE result objects.

### 4.3.1 circlepackPlot

The `circlepackPlot` takes a list of all DE genes from RNA and ADT DE analysis and will plot only the top most significant DE genes to plot.

    rna_DEgenes <- metadata(sce_citeseq)[["DE_res_RNA_filter"]]
    adt_DEgenes <- metadata(sce_citeseq)[["DE_res_ADT_filter"]]

    rna_DEgenes <- lapply(rna_DEgenes, function(x){
      x$name <- gsub("hg19_", "", x$name)
      x})
    DEbubblePlot(list(RNA = rna_DEgenes, ADT = adt_DEgenes))

### 4.3.2 DEcomparisonPlot

For the `DEcomparisonPlot`, as well as a list containing the DE genes for RNA and ADT, a `feature_list` specifying the genes and proteins of interest is required.

    rna_list <- c("hg19_CD4",
                  "hg19_CD8A",
                  "hg19_HLA-DRB1",
                  "hg19_ITGAX",
                  "hg19_NCAM1",
                  "hg19_CD27",
                  "hg19_CD19")

    adt_list <- c("CD4", "CD8", "MHCII (HLA-DR)", "CD11c", "CD56", "CD27", "CD19")

    rna_DEgenes_all <- metadata(sce_citeseq)[["DE_res_RNA"]]
    adt_DEgenes_all <- metadata(sce_citeseq)[["DE_res_ADT"]]

    feature_list <- list(RNA = rna_list, ADT = adt_list)
    de_list <- list(RNA = rna_DEgenes_all, ADT = adt_DEgenes_all)

    DEcomparisonPlot(de_list = de_list,
                     feature_list = feature_list)

# 5 ADT Importance Evaluation

An important evaluation in CITE-seq data analysis is to assess the quality of each ADT and to evaluate the contribution of ADTs towards clustering outcome. CiteFuse calculates the relative importance of ADT towards clustering outcome by using a random forest model. The higher the score of an ADT, the greater its importance towards the final clustering outcome.

    set.seed(2020)
    sce_citeseq <- importanceADT(sce_citeseq,
                                 group = sce_citeseq$SNF_W_louvain,
                                 subsample = TRUE)

    visImportance(sce_citeseq, plot = "boxplot")

    visImportance(sce_citeseq, plot = "heatmap")

    sort(metadata(sce_citeseq)[["importanceADT"]], decreasing = TRUE)[1:20]
    #>              CD27               CD8               CD4      PECAM (CD31)
    #>         41.032868         35.381533         33.494609         12.263987
    #>               CD5              CD28               CD7             CD11b
    #>         12.201304         12.123428         11.791693         10.808128
    #> IL7Ralpha (CD127)    MHCII (HLA-DR)               CD2              CD44
    #>          9.868645          8.657011          8.589313          8.466849
    #>      CD366 (tim3)         HLA-A,B,C             CD11c               CD3
    #>          5.893551          5.832131          4.933470          4.517884
    #>            CD45RA  CD26 (Adenosine)              CD69      PD-1 (CD279)
    #>          4.016464          3.776677          3.734701          3.508469

The importance scores can be visualised in a boxplot and heatmap. Our evaluation of ADT importance show that unsurprisingly CD4 and CD8 are the top two discriminating proteins in PBMCs.

Let us try clustering with only ADTs with a score greater than 5.

    subset_adt <- names(which(metadata(sce_citeseq)[["importanceADT"]] > 5))
    subset_adt
    #>  [1] "CD11b"             "CD2"               "CD27"
    #>  [4] "CD28"              "CD366 (tim3)"      "CD4"
    #>  [7] "CD44"              "CD5"               "CD7"
    #> [10] "CD8"               "HLA-A,B,C"         "IL7Ralpha (CD127)"
    #> [13] "MHCII (HLA-DR)"    "PECAM (CD31)"

    system.time(sce_citeseq <- CiteFuse(sce_citeseq,
                                        ADT_subset = subset_adt,
                                        metadata_names = c("W_SNF_adtSubset1",
                                                           "W_ADT_adtSubset1",
                                                           "W_RNA")))
    #> Calculating affinity matrix
    #> Performing SNF
    #>    user  system elapsed
    #>    2.97    0.07    3.03

    SNF_W_clust_adtSubset1 <- spectralClustering(metadata(sce_citeseq)[["W_SNF_adtSubset1"]], K = 5)
    #> Computing Spectral Clustering
    sce_citeseq$SNF_W_clust_adtSubset1 <- as.factor(SNF_W_clust_adtSubset1$labels)

    library(mclust)
    adjustedRandIndex(sce_citeseq$SNF_W_clust_adtSubset1, sce_citeseq$SNF_W_clust)
    #> [1] 0.8499764

When we compare between the two clustering outcomes, we find that the adjusted rand index is approximately 0.93, where a value of 1 denotes complete concordance.

# 6 Gene - ADT network

The `geneADTnetwork` function plots an interaction network between genes identified from the DE analysis. The nodes denote proteins and RNA whilst the edges denote positive and negative correlation in expression.

    RNA_feature_subset <- unique(as.character(unlist(lapply(rna_DEgenes_all, "[[", "name"))))
    ADT_feature_subset <- unique(as.character(unlist(lapply(adt_DEgenes_all, "[[", "name"))))

    geneADTnetwork(sce_citeseq,
                   RNA_feature_subset = RNA_feature_subset,
                   ADT_feature_subset = ADT_feature_subset,
                   cor_method = "pearson",
                   network_layout = igraph::layout_with_fr)

    #> IGRAPH eb65f0d UN-B 72 134 --
    #> + attr: name (v/c), label (v/c), class (v/c), type (v/l), shape (v/c),
    #> | color (v/c), size (v/n), label.cex (v/n), label.color (v/c), value
    #> | (e/n), color (e/c), weights (e/n)
    #> + edges from eb65f0d (vertex names):
    #>  [1] RNA_hg19_CD8A --ADT_CD4  RNA_hg19_CCL5 --ADT_CD4  RNA_hg19_GNLY --ADT_CD4
    #>  [4] RNA_hg19_KLRD1--ADT_CD4  RNA_hg19_GZMB --ADT_CD4  RNA_hg19_CST7 --ADT_CD4
    #>  [7] RNA_hg19_NKG7 --ADT_CD4  RNA_hg19_CTSW --ADT_CD4  RNA_hg19_LTB  --ADT_CD27
    #> [10] RNA_hg19_TCF7 --ADT_CD27 RNA_hg19_RPL32--ADT_CD27 RNA_hg19_IL7R --ADT_CD27
    #> [13] RNA_hg19_RPL13--ADT_CD27 RNA_hg19_RPL37--ADT_CD27 RNA_hg19_RPS8 --ADT_CD27
    #> [16] RNA_hg19_RPL11--ADT_CD27 RNA_hg19_CD27 --ADT_CD27 RNA_hg19_RPS12--ADT_CD27
    #> + ... omitted several edges

# 7 RNA Ligand - ADT Receptor Analysis

With the advent of CITE-seq, we can now predict ligand-receptor interactions by using cell surface protein expression. CiteFuse implements a `ligandReceptorTest` to find ligand receptor interactions between sender and receiver cells. Importantly, the ADT count is used to predict receptor expression within receiver cells. Note that the setting `altExp_name = "RNA"` would enable users to predict ligand-receptor interaction from RNA expression only.

    data("lr_pair_subset", package = "CiteFuse")
    head(lr_pair_subset)
    #>      [,1]          [,2]
    #> [1,] "hg19_IL17RA" "CD45"
    #> [2,] "hg19_FAS"    "CD11b"
    #> [3,] "hg19_GZMK"   "CD62L"
    #> [4,] "hg19_CD40LG" "CD11b"
    #> [5,] "hg19_FLT3LG" "CD62L"
    #> [6,] "hg19_GZMA"   "CD19"

    sce_citeseq <- normaliseExprs(sce = sce_citeseq,
                                  altExp_name = "ADT",
                                  transform = "zi_minMax")

    sce_citeseq <- normaliseExprs(sce = sce_citeseq,
                                  altExp_name = "none",
                                  exprs_value = "logcounts",
                                  transform = "minMax")

    sce_citeseq <- ligandReceptorTest(sce = sce_citeseq,
                                      ligandReceptor_list = lr_pair_subset,
                                      cluster = sce_citeseq$SNF_W_louvain,
                                      RNA_exprs_value = "minMax",
                                      use_alt_exp = TRUE,
                                      altExp_name = "ADT",
                                      altExp_exprs_value = "zi_minMax",
                                      num_permute = 1000)
    #> 100 ......200 ......300 ......400 ......500 ......600 ......700 ......800 ......900 ......1000 ......

    visLigandReceptor(sce_citeseq,
                      type = "pval_heatmap",
                      receptor_type = "ADT")

    visLigandReceptor(sce_citeseq,
                      type = "pval_dotplot",
                      receptor_type = "ADT")

    visLigandReceptor(sce_citeseq,
                      type = "group_network",
                      receptor_type = "ADT")

    visLigandReceptor(sce_citeseq,
                      type = "group_heatmap",
                      receptor_type = "ADT")

    visLigandReceptor(sce_citeseq,
                      type = "lr_network",
                      receptor_type = "ADT")

# 8 Between-sample analysis

Lastly, we will jointly analyse the current PBMC CITE-seq data, taken from healthy human donors, and another subset of CITE-seq data from patients with cutaneous T-cell lymphoma (CTCL), again from Mimitou et al. (2019). The data `sce_ctcl_subset` provided in our `CiteFuse` package already contains the clustering information.

    data("sce_ctcl_subset", package = "CiteFuse")

To visualise and compare gene or protein expression data, we can use `visualiseExprsList` function.

    visualiseExprsList(sce_list = list(control = sce_citeseq,
                                       ctcl = sce_ctcl_subset),
                       plot = "boxplot",
                       altExp_name = "none",
                       exprs_value = "logcounts",
                       feature_subset = c("hg19_S100A10", "hg19_CD8A"),
                       group_by = c("SNF_W_louvain", "SNF_W_louvain"))

    visualiseExprsList(sce_list = list(control = sce_citeseq,
                                       ctcl = sce_ctcl_subset),
                       plot = "boxplot",
                       altExp_name = "ADT",
                       feature_subset = c("CD19", "CD8"),
                       group_by = c("SNF_W_louvain", "SNF_W_louvain"))

We can then perform differential expression analysis of the RNA expression level across the two clusters that have high CD19 expression in ADT.

    de_res <- DEgenesCross(sce_list = list(control = sce_citeseq,
                                           ctcl = sce_ctcl_subset),
                           colData_name = c("SNF_W_louvain", "SNF_W_louvain"),
                           group_to_test = c("2", "6"))
    de_res_filter <- selectDEgenes(de_res = de_res)
    de_res_filter
    #> $control
    #>             stats.W         pval     p.adjust meanExprs.1 meanExprs.2
    #> hg19_GNLY      28.0 6.646869e-23 6.646869e-23  0.07220401    4.591530
    #> hg19_CST7     149.5 4.403301e-21 4.403301e-21  0.22512314    2.640981
    #> hg19_NKG7     165.0 8.951222e-21 8.951222e-21  0.51066894    3.815105
    #> hg19_GZMH     301.0 1.984392e-19 1.984392e-19  0.00000000    1.972667
    #> hg19_GZMB     474.0 3.856340e-17 3.856340e-17  0.11667246    2.017016
    #> hg19_CCL5     474.0 1.010639e-16 1.010639e-16  1.04348029    3.772472
    #> hg19_FGFBP2   548.0 1.623043e-16 1.623043e-16  0.03101677    1.666924
    #> hg19_KLRD1    574.5 4.662465e-16 4.662465e-16  0.09327555    1.568567
    #> hg19_EFHD2    794.0 5.121771e-14 5.121771e-14  0.02064028    1.212213
    #> hg19_PRF1     801.5 9.024284e-14 9.024284e-14  0.06473009    1.467354
    #>              meanPct.1 meanPct.2 meanDiff   pctDiff        name   group
    #> hg19_GNLY   0.02325581 0.9927007 4.519326 0.9694449   hg19_GNLY control
    #> hg19_CST7   0.13953488 0.9927007 2.415857 0.8531658   hg19_CST7 control
    #> hg19_NKG7   0.30232558 1.0000000 3.304436 0.6976744   hg19_NKG7 control
    #> hg19_GZMH   0.00000000 0.8978102 1.972667 0.8978102   hg19_GZMH control
    #> hg19_GZMB   0.06976744 0.8832117 1.900344 0.8134442   hg19_GZMB control
    #> hg19_CCL5   0.37209302 1.0000000 2.728992 0.6279070   hg19_CCL5 control
    #> hg19_FGFBP2 0.02325581 0.8248175 1.635907 0.8015617 hg19_FGFBP2 control
    #> hg19_KLRD1  0.09302326 0.8321168 1.475292 0.7390935  hg19_KLRD1 control
    #> hg19_EFHD2  0.02325581 0.7372263 1.191573 0.7139705  hg19_EFHD2 control
    #> hg19_PRF1   0.04651163 0.7591241 1.402624 0.7126125   hg19_PRF1 control
    #>
    #> $ctcl
    #>               stats.W         pval     p.adjust meanExprs.1 meanExprs.2
    #> hg19_LTB        368.0 6.626368e-28 6.626368e-28   0.1049424    1.978730
    #> hg19_RPS26        0.0 4.957273e-23 4.957273e-23   1.6292756    5.072471
    #> hg19_IL7R       747.0 5.147332e-21 5.147332e-21   0.1597474    1.651591
    #> hg19_SELL      1006.0 3.198651e-20 3.198651e-20   0.0659935    1.152572
    #> hg19_LEPROTL1   699.0 1.893136e-18 1.893136e-18   0.2381900    1.300745
    #> hg19_EEF1B2     512.5 3.188785e-16 3.188785e-16   1.7119640    3.043623
    #> hg19_HBB       1712.5 1.982149e-15 1.982149e-15   0.0000000    0.431026
    #> hg19_NOSIP     1034.0 2.135217e-14 2.135217e-14   0.2014222    1.157235
    #> hg19_FOS       1278.5 1.229947e-13 1.229947e-13   0.1492607    1.125337
    #> hg19_NPM1       927.0 1.013945e-11 1.013945e-11   1.1724979    2.202268
    #>                meanPct.1 meanPct.2  meanDiff   pctDiff          name group
    #> hg19_LTB      0.08029197 0.9069767 1.8737878 0.8266848      hg19_LTB  ctcl
    #> hg19_RPS26    0.89051095 1.0000000 3.4431949 0.1094891    hg19_RPS26  ctcl
    #> hg19_IL7R     0.10218978 0.8139535 1.4918432 0.7117637     hg19_IL7R  ctcl
    #> hg19_SELL     0.05109489 0.6976744 1.0865780 0.6465795     hg19_SELL  ctcl
    #> hg19_LEPROTL1 0.18978102 0.9069767 1.0625555 0.7171957 hg19_LEPROTL1  ctcl
    #> hg19_EEF1B2   0.86131387 0.9767442 1.3316593 0.1154303   hg19_EEF1B2  ctcl
    #> hg19_HBB      0.00000000 0.4186047 0.4310260 0.4186047      hg19_HBB  ctcl
    #> hg19_NOSIP    0.19708029 0.7674419 0.9558133 0.5703616    hg19_NOSIP  ctcl
    #> hg19_FOS      0.11678832 0.6511628 0.9760767 0.5343745      hg19_FOS  ctcl
    #> hg19_NPM1     0.72992701 0.9534884 1.0297701 0.2235614     hg19_NPM1  ctcl

# 9 Read data from 10X Genomics

Readers unfamiliar with the workflow of converting a count matrix into a `SingleCellExperiment` object may use the `readFrom10X` function to convert count matrix from a 10X experiment into an object that can be used for all functions in CiteFuse.

    tmpdir <- tempdir()
    download.file("http://cf.10xgenomics.com/samples/cell-exp/3.1.0/connect_5k_pbmc_NGSC3_ch1/connect_5k_pbmc_NGSC3_ch1_filtered_feature_bc_matrix.tar.gz", file.path(tmpdir, "/5k_pbmc_NGSC3_ch1_filtered_feature_bc_matrix.tar.gz"))
    untar(file.path(tmpdir, "5k_pbmc_NGSC3_ch1_filtered_feature_bc_matrix.tar.gz"),
          exdir = tmpdir)
    sce_citeseq_10X <- readFrom10X(file.path(tmpdir, "filtered_feature_bc_matrix/"))
    sce_citeseq_10X

# 10 SessionInfo

    sessionInfo()
    #> R version 4.3.0 RC (2023-04-13 r84269 ucrt)
    #> Platform: x86_64-w64-mingw32/x64 (64-bit)
    #> Running under: Windows Server 2022 x64 (build 20348)
    #>
    #> Matrix products: default
    #>
    #>
    #> locale:
    #> [1] LC_COLLATE=C
    #> [2] LC_CTYPE=English_United States.utf8
    #> [3] LC_MONETARY=English_United States.utf8
    #> [4] LC_NUMERIC=C
    #> [5] LC_TIME=English_United States.utf8
    #>
    #> time zone: America/New_York
    #> tzcode source: internal
    #>
    #> attached base packages:
    #> [1] stats4    stats     graphics  grDevices utils     datasets  methods
    #> [8] base
    #>
    #> other attached packages:
    #>  [1] mclust_6.0.0                gridExtra_2.3
    #>  [3] DT_0.27                     scater_1.28.0
    #>  [5] ggplot2_3.4.2               scuttle_1.10.0
    #>  [7] SingleCellExperiment_1.22.0 SummarizedExperiment_1.30.0
    #>  [9] Biobase_2.60.0              GenomicRanges_1.52.0
    #> [11] GenomeInfoDb_1.36.0         IRanges_2.34.0
    #> [13] S4Vectors_0.38.0            BiocGenerics_0.46.0
    #> [15] MatrixGenerics_1.12.0       matrixStats_0.63.0
    #> [17] CiteFuse_1.12.0             BiocStyle_2.28.0
    #>
    #> loaded via a namespace (and not attached):
    #>   [1] RColorBrewer_1.1-3        tensorA_0.36.2
    #>   [3] jsonlite_1.8.4            magrittr_2.0.3
    #>   [5] magick_2.7.4              ggbeeswarm_0.7.1
    #>   [7] farver_2.1.1              rmarkdown_2.21
    #>   [9] zlibbioc_1.46.0           vctrs_0.6.2
    #>  [11] DelayedMatrixStats_1.22.0 RCurl_1.98-1.12
    #>  [13] htmltools_0.5.5           BiocNeighbors_1.18.0
    #>  [15] Rhdf5lib_1.22.0           rhdf5_2.44.0
    #>  [17] sass_0.4.5                bslib_0.4.2
    #>  [19] htmlwidgets_1.6.2         plyr_1.8.8
    #>  [21] plotly_4.10.1             cachem_1.0.7
    #>  [23] igraph_1.4.2              lifecycle_1.0.3
    #>  [25] pkgconfig_2.0.3           rsvd_1.0.5
    #>  [27] Matrix_1.5-4              R6_2.5.1
    #>  [29] fastmap_1.1.1             GenomeInfoDbData_1.2.10
    #>  [31] digest_0.6.31             colorspace_2.1-0
    #>  [33] dqrng_0.3.0               irlba_2.3.5.1
    #>  [35] crosstalk_1.2.0           beachmat_2.16.0
    #>  [37] labeling_0.4.2            randomForest_4.7-1.1
    #>  [39] fansi_1.0.4               httr_1.4.5
    #>  [41] polyclip_1.10-4           compiler_4.3.0
    #>  [43] withr_2.5.0               BiocParallel_1.34.0
    #>  [45] viridis_0.6.2             highr_0.10
    #>  [47] ggforce_0.4.1             MASS_7.3-59
    #>  [49] bayesm_3.1-5              DelayedArray_0.26.0
    #>  [51] bluster_1.10.0            tools_4.3.0
    #>  [53] vipor_0.4.5               beeswarm_0.4.0
    #>  [55] glue_1.6.2                dbscan_1.1-11
    #>  [57] nlme_3.1-162              rhdf5filters_1.12.0
    #>  [59] grid_4.3.0                Rtsne_0.16
    #>  [61] cluster_2.1.4             reshape2_1.4.4
    #>  [63] generics_0.1.3            gtable_0.3.3
    #>  [65] tidyr_1.3.0               data.table_1.14.8
    #>  [67] BiocSingular_1.16.0       tidygraph_1.2.3
    #>  [69] ScaledMatrix_1.8.0        metapod_1.8.0
    #>  [71] utf8_1.2.3                XVector_0.40.0
    #>  [73] ggrepel_0.9.3             pillar_1.9.0
    #>  [75] stringr_1.5.0             limma_3.56.0
    #>  [77] robustbase_0.95-1         splines_4.3.0
    #>  [79] dplyr_1.1.2               tweenr_2.0.2
    #>  [81] lattice_0.21-8            FNN_1.1.3.2
    #>  [83] survival_3.5-5            compositions_2.0-6
    #>  [85] tidyselect_1.2.0          locfit_1.5-9.7
    #>  [87] knitr_1.42                bookdown_0.33
    #>  [89] edgeR_3.42.0              xfun_0.39
    #>  [91] graphlayouts_0.8.4        mixtools_2.0.0
    #>  [93] statmod_1.5.0             DEoptimR_1.0-12
    #>  [95] pheatmap_1.0.12           stringi_1.7.12
    #>  [97] lazyeval_0.2.2            yaml_2.3.7
    #>  [99] evaluate_0.20             codetools_0.2-19
    #> [101] kernlab_0.9-32            ggraph_2.1.0
    #> [103] tibble_3.2.1              BiocManager_1.30.20
    #> [105] cli_3.6.1                 uwot_0.1.14
    #> [107] segmented_1.6-4           munsell_0.5.0
    #> [109] jquerylib_0.1.4           Rcpp_1.0.10
    #> [111] parallel_4.3.0            ellipsis_0.3.2
    #> [113] scran_1.28.0              sparseMatrixStats_1.12.0
    #> [115] bitops_1.0-7              viridisLite_0.4.1
    #> [117] scales_1.2.1              ggridges_0.5.4
    #> [119] purrr_1.0.1               rlang_1.1.0
    #> [121] cowplot_1.1.1