Author: Jayaram Kancherla, Kazi Tasnim Zinat, Héctor Corrada Bravo

# Interactively explore and visualize Single Cell RNA seq data

# Introduction

`scTreeViz` is a package for interactive visualization and exploration of Single Cell RNA sequencing data. `scTreeViz` provides methods for exploring hierarchical features (eg. clusters in single cell at different resolutions or taxonomic hierarchy in single cell datasets), while supporting other useful data visualization charts like heatmaps for expression and scatter plots for dimensionality reductions like UMAP or TSNE.

## Loading required packages

    library(scTreeViz)
    library(Seurat)
    library(SC3)
    library(scran)
    library(scater)
    library(clustree)
    library(igraph)
    library(scRNAseq)

# Preparing Datasets

The first step in using the `scTreeViz` package is to wrap datasets into `TreeViz` objects. The `TreeViz` class extends `SummarizedExperiment` and provides various methods to interactively perform various operations on the underlying hierarchy and count or expression matrices. In this section, we show various ways to generate a `TreeViz` object either from existing Single Cell packages (SingleCellExperiment or Seurat) or from a raw count matrix and cluster hierarchy.

## From `SingleCellExperiment`

A number of Single cell datasets are available as `SingleCellExperiment` objects through the `scRNAseq` package, for this usecase, we use `LunSpikeInData` dataset. In addition, we calculate the dimensionality reductions; UMAP, TSNE and PCA from the functions provided in `scater` package.

    # load dataset
    sce<- ZeiselBrainData()
    # Normalization
    sce <- logNormCounts(sce)
    # calculate umap and tsne
    sce <- runUMAP(sce)
    sce<- runTSNE(sce)
    sce<- runPCA(sce)

We provide `createFromSCE` function to create a `TreeViz` object from `SingleCellExperiment` object. Here, the workflow works in two ways:

1. If no cluster information is available in the `colData` of the `SingleCellExperiment` object, we create clusters at different resolutions using the `WalkTrap` algorithm by calling an internal function `generate_walktrap_hierarchy` and use this cluster information for visualization.

    treeViz <- createFromSCE(sce, reduced_dim = c("UMAP","PCA","TSNE"))
    #>  [1] "1.cluster1"   "2.cluster2"   "3.cluster3"   "4.cluster4"   "5.cluster5"
    #>  [6] "6.cluster6"   "7.cluster7"   "8.cluster8"   "9.cluster9"   "10.cluster10"
    #> [11] "11.cluster11" "12.cluster12" "13.cluster13" "14.cluster14" "15.cluster15"
    #> [16] "samples"
    plot(treeViz)

2. If cluster information is provided in the `colData` of the object, then the user should set the flag parameter `check_coldata` to `TRUE` and provide prefix for the columns where cluster information is stored.

    # Forming clusters
    set.seed(1000)
    for (i in  seq(10)) {
      clust.kmeans <- kmeans(reducedDim(sce, "TSNE"), centers = i)
      sce[[paste0("clust", i)]] <- factor(clust.kmeans$cluster)
    }

    treeViz<- createFromSCE(sce, check_coldata = TRUE, col_regex = "clust")
    plot(treeViz)

Note: In both cases the user needs to provide the name of dimensionality reductions present in the object as a parameter.

## From `Seurat`

We use the dataset `pbmc_small` available through Seurat to create a `TreeViz` object.

    data(pbmc_small)
    pbmc <- pbmc_small

We then preprocess the data and find clusters at different resolutions.

    pbmc[["percent.mt"]] <- PercentageFeatureSet(pbmc, pattern = "^MT-")
    pbmc <- NormalizeData(pbmc)
    all.genes <- rownames(pbmc)
    pbmc <- ScaleData(pbmc, vars.to.regress = "percent.mt")
    pbmc <- FindVariableFeatures(object = pbmc)
    pbmc <- RunPCA(pbmc, features = VariableFeatures(object = pbmc))
    pbmc <- FindNeighbors(pbmc, dims = 1:10)
    pbmc <- FindClusters(pbmc, resolution = c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0), print.output = 0, save.SNN = TRUE)
    pbmc

The measurements for dimensionality reduction methods we want to visualize are also added to the object via native functions in `Seurat`. Since `PCA` is already added, we calculate `TSNE` and `UMAP`

    # pbmc<- RunTSNE(pbmc)
    pbmc<- RunUMAP(pbmc, dims=1:3)
    Reductions(pbmc)

We use the `createFromSeurat` function to create a `TreeViz` object from `Seurat` object. In addition the object, we pass the name of dimensionality reductions present in the object as a paramter in vector format to indicate these measurements should be added to `treeviz` for visualization. If the mentioned reduced dimension is not present it would simply be ignored.

    treeViz<- createFromSeurat(pbmc, check_metadata = TRUE, reduced_dim = c("umap","pca","tsne"))
    #> [1] "6.cluster6"   "10.cluster10" "11.cluster11" "samples"
    #> [1] "umap" "pca"  "tsne"
    plot(treeViz)

## Create TreeViz from count matrix and Cluster hierarchy

    n=64
    # create a hierarchy
    df<- data.frame(cluster0=rep(1,n))
    for(i in seq(1,5)){
      df[[paste0("cluster",i)]]<- rep(seq(1:(2**i)),each=ceiling(n/(2**i)),len=n)
    }

    # generate a count matrix
    counts <- matrix(rpois(6400, lambda = 10), ncol=n, nrow=100)
    colnames(counts)<- seq(1:64)
    # create a `TreeViz` object
    treeViz <- createTreeViz(df, counts)
    plot(treeViz)

## Start the TreeViz App (using hosted app)

Start the App from the `treeViz` object we created. This adds a `facetZoom` to navigate the cluster hierarchy, a heatmap of the top `n` most variable genes from the dataset, where ‘n’ is selected by the user and one scatter plot for each of the reduced dimensions.

    app <- startTreeviz(treeViz, top_genes = 500)

Cell 416B dataset
pbmc dataset

Users can also use the interface to explore the same dataset using different visualizations available through Epiviz.

## Visualize gene expression across clusters

Users can also add Gene Box plots using either the frontend application, or from R session. In the following example, we visualize the 5th, 50th and 500th most variable gene as Box plots

### Adding Gene Box Plots via UI

Users need to select `Add Visualization -> Gene Box PLot` option from menu and then select the desired gene using the search pane in the appeared dialogue box

Selecting a gene to visualize

### Adding Gene Box Plots via R Session

Users can also select the gene from R session by using the `plotGene` command followed by Gene name.

    app$plotGene(gene="AIF1")

## Stop App

After exploring the dataset, this command the websocket connection.

    app$stop_app()

## Session

    sessionInfo()
    #> R version 4.6.0 (2026-04-24)
    #> Platform: x86_64-pc-linux-gnu
    #> Running under: Ubuntu 24.04.4 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.26.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] stats4    stats     graphics  grDevices utils     datasets  methods
    #> [8] base
    #>
    #> other attached packages:
    #>  [1] future_1.70.0               scRNAseq_2.27.0
    #>  [3] igraph_2.3.1                clustree_0.5.1
    #>  [5] ggraph_2.2.2                scater_1.41.1
    #>  [7] ggplot2_4.0.3               scran_1.41.1
    #>  [9] scuttle_1.23.1              SingleCellExperiment_1.35.1
    #> [11] SC3_1.41.0                  Seurat_5.5.0
    #> [13] SeuratObject_5.4.0          sp_2.2-1
    #> [15] scTreeViz_1.19.0            SummarizedExperiment_1.43.0
    #> [17] Biobase_2.73.1              GenomicRanges_1.65.0
    #> [19] Seqinfo_1.3.0               IRanges_2.47.2
    #> [21] S4Vectors_0.51.3            BiocGenerics_0.59.6
    #> [23] generics_0.1.4              MatrixGenerics_1.25.0
    #> [25] matrixStats_1.5.0           epivizr_2.43.0
    #> [27] BiocStyle_2.41.0
    #>
    #> loaded via a namespace (and not attached):
    #>   [1] epivizrData_1.41.0       goftest_1.2-3            Biostrings_2.81.2
    #>   [4] HDF5Array_1.41.0         vctrs_0.7.3              spatstat.random_3.5-0
    #>   [7] digest_0.6.39            png_0.1-9                proxy_0.4-29
    #>  [10] pcaPP_2.0-5              BiocBaseUtils_1.15.1     gypsum_1.9.0
    #>  [13] ggrepel_0.9.8            deldir_2.0-4             parallelly_1.47.0
    #>  [16] alabaster.sce_1.13.0     MASS_7.3-65              reshape2_1.4.5
    #>  [19] httpuv_1.6.17            foreach_1.5.2            withr_3.0.2
    #>  [22] xfun_0.57                survival_3.8-6           doRNG_1.8.6.3
    #>  [25] memoise_2.0.1            ggbeeswarm_0.7.3         zoo_1.8-15
    #>  [28] pbapply_1.7-4            DEoptimR_1.1-4           sys_3.4.3
    #>  [31] KEGGREST_1.53.0          promises_1.5.0           otel_0.2.0
    #>  [34] httr_1.4.8               restfulr_0.0.16          globals_0.19.1
    #>  [37] fitdistrplus_1.2-6       rhdf5filters_1.25.0      rhdf5_2.57.1
    #>  [40] UCSC.utils_1.9.0         miniUI_0.1.2             curl_7.1.0
    #>  [43] ScaledMatrix_1.21.0      h5mread_1.5.0            polyclip_1.10-7
    #>  [46] ExperimentHub_3.3.0      SparseArray_1.13.2       RBGL_1.89.0
    #>  [49] xtable_1.8-8             stringr_1.6.0            doParallel_1.0.17
    #>  [52] evaluate_1.0.5           S4Arrays_1.13.0          BiocFileCache_3.3.0
    #>  [55] irlba_2.3.7              filelock_1.0.3           ROCR_1.0-12
    #>  [58] reticulate_1.46.0        spatstat.data_3.1-9      magrittr_2.0.5
    #>  [61] lmtest_0.9-40            later_1.4.8              buildtools_1.0.0
    #>  [64] viridis_0.6.5            lattice_0.22-9           spatstat.geom_3.8-1
    #>  [67] future.apply_1.20.2      robustbase_0.99-7        scattermore_1.2
    #>  [70] XML_3.99-0.23            cowplot_1.2.0            RcppAnnoy_0.0.23
    #>  [73] maketools_1.3.2          class_7.3-23             pillar_1.11.1
    #>  [76] nlme_3.1-169             iterators_1.0.14         compiler_4.6.0
    #>  [79] beachmat_2.29.0          RSpectra_0.16-2          stringi_1.8.7
    #>  [82] tensor_1.5.1             GenomicAlignments_1.49.0 plyr_1.8.9
    #>  [85] crayon_1.5.3             abind_1.4-8              BiocIO_1.23.3
    #>  [88] locfit_1.5-9.12          graphlayouts_1.2.3       bit_4.6.0
    #>  [91] dplyr_1.2.1              codetools_0.2-20         BiocSingular_1.29.0
    #>  [94] bslib_0.11.0             alabaster.ranges_1.13.0  e1071_1.7-17
    #>  [97] plotly_4.12.0            mime_0.13                splines_4.6.0
    #> [100] Rcpp_1.1.1-1.1           fastDummies_1.7.6        dbplyr_2.5.2
    #> [103] knitr_1.51               blob_1.3.0               BiocVersion_3.24.0
    #> [106] AnnotationFilter_1.37.0  WriteXLS_6.8.0           checkmate_2.3.4
    #> [109] listenv_0.10.1           tibble_3.3.1             Matrix_1.7-5
    #> [112] statmod_1.5.2            tweenr_2.0.3             pkgconfig_2.0.3
    #> [115] pheatmap_1.0.13          tools_4.6.0              cachem_1.1.0
    #> [118] cigarillo_1.3.0          RSQLite_3.53.1           viridisLite_0.4.3
    #> [121] DBI_1.3.0                fastmap_1.2.0            rmarkdown_2.31
    #> [124] scales_1.4.0             grid_4.6.0               ica_1.0-3
    #> [127] epivizrServer_1.41.0     Rsamtools_2.29.0         AnnotationHub_4.3.0
    #> [130] sass_0.4.10              FNN_1.1.4.1              patchwork_1.3.2
    #> [133] BiocManager_1.30.27      dotCall64_1.2            graph_1.91.0
    #> [136] RANN_2.6.2               alabaster.schemas_1.13.0 farver_2.1.2
    #> [139] tidygraph_1.3.1          yaml_2.3.12              rtracklayer_1.73.0
    #> [142] cli_3.6.6                purrr_1.2.2              lifecycle_1.0.5
    #> [145] uwot_0.2.4               mvtnorm_1.3-7            backports_1.5.1
    #> [148] bluster_1.23.0           BiocParallel_1.47.0      gtable_0.3.6
    #> [151] rjson_0.2.23             ggridges_0.5.7           progressr_0.19.0
    #> [154] parallel_4.6.0           limma_3.69.1             jsonlite_2.0.0
    #> [157] edgeR_4.11.1             RcppHNSW_0.7.0           bitops_1.0-9
    #> [160] bit64_4.8.2              Rtsne_0.17               alabaster.matrix_1.13.0
    #> [163] spatstat.utils_3.2-3     BiocNeighbors_2.7.2      jquerylib_0.1.4
    #> [166] metapod_1.21.0           alabaster.se_1.13.0      dqrng_0.4.1
    #> [169] spatstat.univar_3.2-0    rrcov_1.7-7              lazyeval_0.2.3
    #> [172] alabaster.base_1.13.0    shiny_1.13.0             htmltools_0.5.9
    #> [175] sctransform_0.4.3        rappdirs_0.3.4           ensembldb_2.37.1
    #> [178] glue_1.8.1               spam_2.11-3              httr2_1.2.2
    #> [181] XVector_0.53.0           RCurl_1.98-1.18          gridExtra_2.3
    #> [184] R6_2.6.1                 tidyr_1.3.2              labeling_0.4.3
    #> [187] GenomicFeatures_1.65.0   cluster_2.1.8.2          rngtools_1.5.2
    #> [190] Rhdf5lib_2.1.0           GenomeInfoDb_1.49.1      DelayedArray_0.39.3
    #> [193] tidyselect_1.2.1         vipor_0.4.7              ProtGenerics_1.45.0
    #> [196] ggforce_0.5.0            AnnotationDbi_1.75.0     rsvd_1.0.5
    #> [199] KernSmooth_2.23-26       S7_0.2.2                 data.table_1.18.4
    #> [202] htmlwidgets_1.6.4        RColorBrewer_1.1-3       rlang_1.2.0
    #> [205] spatstat.sparse_3.2-0    spatstat.explore_3.8-1   beeswarm_0.4.0
    #> [208] OrganismDbi_1.55.1