# scClassify Model Building and Prediction

Yingxin Lin1,2

1School of Mathematics and Statistics, The University of Sydney, Australia
2Charles Perkins Centre, The University of Sydney, Australia

#### 7 October 2025

# 1 Introduction

A common application of single-cell RNA sequencing (RNA-seq) data is to identify discrete cell types. To take advantage of the large collection of well-annotated scRNA-seq datasets, `scClassify` package implements a set of methods to perform accurate cell type classification based on ensemble learning and sample size calculation. This vignette demonstrates the usage of `scClassify`, providing a pithy description of each method with workable examples.

First, install `scClassify` via `BiocManager`.

    # installation of scClassify
    if (!requireNamespace("BiocManager", quietly = TRUE)) {
        install.packages("BiocManager")
    }
    BiocManager::install("scClassify")

# 2 Setting up the data

We assume that you have log-transformed (size-factor normalized) matrices where each row is a gene and each column a cell for a reference dataset and a query dataset. For demonstration purposes, we will take a subset of single-cell pancreas datasets from two independent studies (Wang et al., and Xin et al.).

    library("scClassify")
    data("scClassify_example")
    xin_cellTypes <- scClassify_example$xin_cellTypes
    exprsMat_xin_subset <- scClassify_example$exprsMat_xin_subset
    wang_cellTypes <- scClassify_example$wang_cellTypes
    exprsMat_wang_subset <- scClassify_example$exprsMat_wang_subset
    exprsMat_xin_subset <- as(exprsMat_xin_subset, "dgCMatrix")
    exprsMat_wang_subset <- as(exprsMat_wang_subset, "dgCMatrix")

The original cell type annotations and compositions of the example datasets can be easily accessed as shown below.

    table(xin_cellTypes)
    #> xin_cellTypes
    #> alpha  beta delta gamma
    #>   285   261    49    79
    table(wang_cellTypes)
    #> wang_cellTypes
    #>   acinar    alpha     beta    delta   ductal    gamma stellate
    #>        5      206      118       10       96       21       45

We can see that Xin et al. data only have 4 cell types, while Wang et al. has 7 cell types.

# 3 scClassify

## 3.1 Non-ensemble scClassify

We first perform non-ensemble `scClassify` by using Xin et al. as our reference dataset and Wang et al. data as ur query data. We use `WKNN` as the KNN algorithm, `DE` (differential expression genes) as the gene selection method, and lastly `pearson` as the similarity calculation method.

    scClassify_res <- scClassify(exprsMat_train = exprsMat_xin_subset,
                                 cellTypes_train = xin_cellTypes,
                                 exprsMat_test = list(wang = exprsMat_wang_subset),
                                 cellTypes_test = list(wang = wang_cellTypes),
                                 tree = "HOPACH",
                                 algorithm = "WKNN",
                                 selectFeatures = c("limma"),
                                 similarity = c("pearson"),
                                 returnList = FALSE,
                                 verbose = FALSE)

We can check the cell type tree generated by the reference data:

    scClassify_res$trainRes
    #> Class: scClassifyTrainModel
    #> Model name: training
    #> Feature selection methods: limma
    #> Number of cells in the training data: 674
    #> Number of cell types in the training data: 4
    plotCellTypeTree(cellTypeTree(scClassify_res$trainRes))

Noted that `scClassify_res$trainRes` is a `scClassifyTrainModel` class.

Check the prediction results.

    table(scClassify_res$testRes$wang$pearson_WKNN_limma$predRes, wang_cellTypes)
    #>                   wang_cellTypes
    #>                    acinar alpha beta delta ductal gamma stellate
    #>   alpha                 0   206    0     0      0     2        0
    #>   beta                  0     0  118     0      1     0        0
    #>   beta_delta_gamma      0     0    0     0     25     0        0
    #>   delta                 0     0    0    10      0     0        0
    #>   gamma                 0     0    0     0      0    19        0
    #>   unassigned            5     0    0     0     70     0       45

## 3.2 Ensemble Classify

We next perform ensemble `scClassify` by using Xin et al. as our reference dataset and Wang et al. data as our query data. We use `WKNN` as the KNN algorithm, `DE` as the gene selection method, and `pearson` and `spearman` as the similarity calculation methods. Thus, we will generate two combinations of gene selection models and similarity metrics as training classifiers:

1. `WKNN` + `DE` + `pearson`
2. `WKNN` + `DE` + `spearman`

Here, we will weight these two classifiers equally by setting `weighted_ensemble = FALSE`. By default this is set as `TRUE`, so each base classifier will be weighted by the accuracy rates trained in the reference data.

    scClassify_res_ensemble <- scClassify(exprsMat_train = exprsMat_xin_subset,
                                          cellTypes_train = xin_cellTypes,
                                          exprsMat_test = list(wang = exprsMat_wang_subset),
                                          cellTypes_test = list(wang = wang_cellTypes),
                                          tree = "HOPACH",
                                          algorithm = "WKNN",
                                          selectFeatures = c("limma"),
                                          similarity = c("pearson", "cosine"),
                                          weighted_ensemble = FALSE,
                                          returnList = FALSE,
                                          verbose = FALSE)

We can compare the two base classifiers predictions as below.

    table(scClassify_res_ensemble$testRes$wang$pearson_WKNN_limma$predRes,
          scClassify_res_ensemble$testRes$wang$cosine_WKNN_limma$predRes)
    #>
    #>                    alpha beta beta_delta_gamma delta gamma unassigned
    #>   alpha              208    0                0     0     0          0
    #>   beta                 0  119                0     0     0          0
    #>   beta_delta_gamma     0    0               22     0     0          3
    #>   delta                0    0                0    10     0          0
    #>   gamma                0    0                0     0    19          0
    #>   unassigned           1    3               15     0     0        101

Now, check the final ensemble results:

    table(scClassify_res_ensemble$testRes$wang$ensembleRes$cellTypes,
          wang_cellTypes)
    #>                   wang_cellTypes
    #>                    acinar alpha beta delta ductal gamma stellate
    #>   alpha                 0   206    0     0      0     2        0
    #>   beta                  0     0  118     0      1     0        0
    #>   beta_delta_gamma      0     0    0     0     25     0        0
    #>   delta                 0     0    0    10      0     0        0
    #>   gamma                 0     0    0     0      0    19        0
    #>   unassigned            5     0    0     0     70     0       45

# 4 Train your own model

You can also train your own model `scClassifyTrainModel` using `train_scClassify()`. Note that by setting `weightsCal = TRUE`, we will calculate the training error of the reference data as the weights for the individual classifiers.

Here, we illustrate the training function with gene selection methods based on differential expression (“limma”) and biomodal distribution (“BI”).

    trainClass <- train_scClassify(exprsMat_train = exprsMat_xin_subset,
                                   cellTypes_train = xin_cellTypes,
                                   selectFeatures = c("limma", "BI"),
                                   returnList = FALSE
    )
    #> after filtering not expressed genes
    #> [1] 980 674
    #> [1] "Feature Selection..."
    #> [1] "Number of genes selected to construct HOPACH tree 160"
    #> [1] "Constructing tree ..."
    #> [1] "Training...."
    #> [1] "=== selecting features by: limma ===="
    #> [1] "=== selecting features by: BI ===="

    trainClass
    #> Class: scClassifyTrainModel
    #> Model name: training
    #> Feature selection methods: limma BI
    #> Number of cells in the training data: 674
    #> Number of cell types in the training data: 4

# 5 Session Info

    sessionInfo()
    #> R version 4.5.1 Patched (2025-09-10 r88807)
    #> Platform: aarch64-apple-darwin20
    #> Running under: macOS Ventura 13.7.7
    #>
    #> Matrix products: default
    #> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib
    #> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
    #>
    #> locale:
    #> [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
    #>
    #> time zone: America/New_York
    #> tzcode source: internal
    #>
    #> attached base packages:
    #> [1] stats     graphics  grDevices utils     datasets  methods   base
    #>
    #> other attached packages:
    #> [1] scClassify_1.21.0 BiocStyle_2.37.1
    #>
    #> loaded via a namespace (and not attached):
    #>   [1] gridExtra_2.3               rlang_1.1.6
    #>   [3] magrittr_2.0.4              matrixStats_1.5.0
    #>   [5] compiler_4.5.1              mgcv_1.9-3
    #>   [7] DelayedMatrixStats_1.31.0   vctrs_0.6.5
    #>   [9] reshape2_1.4.4              stringr_1.5.2
    #>  [11] pkgconfig_2.0.3             crayon_1.5.3
    #>  [13] fastmap_1.2.0               magick_2.9.0
    #>  [15] XVector_0.49.1              labeling_0.4.3
    #>  [17] ggraph_2.2.2                rmarkdown_2.30
    #>  [19] tinytex_0.57                purrr_1.1.0
    #>  [21] xfun_0.53                   cachem_1.1.0
    #>  [23] jsonlite_2.0.0              rhdf5filters_1.21.0
    #>  [25] DelayedArray_0.35.3         Rhdf5lib_1.31.0
    #>  [27] BiocParallel_1.43.4         tweenr_2.0.3
    #>  [29] parallel_4.5.1              cluster_2.1.8.1
    #>  [31] R6_2.6.1                    bslib_0.9.0
    #>  [33] stringi_1.8.7               RColorBrewer_1.1-3
    #>  [35] limma_3.65.5                diptest_0.77-2
    #>  [37] GenomicRanges_1.61.5        jquerylib_0.1.4
    #>  [39] Rcpp_1.1.0                  Seqinfo_0.99.2
    #>  [41] bookdown_0.45               SummarizedExperiment_1.39.2
    #>  [43] knitr_1.50                  mixtools_2.0.0.1
    #>  [45] IRanges_2.43.5              Matrix_1.7-4
    #>  [47] splines_4.5.1               igraph_2.1.4
    #>  [49] tidyselect_1.2.1            dichromat_2.0-0.1
    #>  [51] abind_1.4-8                 yaml_2.3.10
    #>  [53] hopach_2.69.0               viridis_0.6.5
    #>  [55] codetools_0.2-20            minpack.lm_1.2-4
    #>  [57] Cepo_1.15.0                 lattice_0.22-7
    #>  [59] tibble_3.3.0                plyr_1.8.9
    #>  [61] Biobase_2.69.1              withr_3.0.2
    #>  [63] S7_0.2.0                    evaluate_1.0.5
    #>  [65] survival_3.8-3              proxy_0.4-27
    #>  [67] polyclip_1.10-7             kernlab_0.9-33
    #>  [69] pillar_1.11.1               BiocManager_1.30.26
    #>  [71] MatrixGenerics_1.21.0       stats4_4.5.1
    #>  [73] plotly_4.11.0               generics_0.1.4
    #>  [75] S4Vectors_0.47.4            ggplot2_4.0.0
    #>  [77] sparseMatrixStats_1.21.0    scales_1.4.0
    #>  [79] glue_1.8.0                  lazyeval_0.2.2
    #>  [81] proxyC_0.5.2                tools_4.5.1
    #>  [83] data.table_1.17.8           graphlayouts_1.2.2
    #>  [85] tidygraph_1.3.1             rhdf5_2.53.5
    #>  [87] grid_4.5.1                  tidyr_1.3.1
    #>  [89] SingleCellExperiment_1.31.1 nlme_3.1-168
    #>  [91] patchwork_1.3.2             ggforce_0.5.0
    #>  [93] HDF5Array_1.37.0            cli_3.6.5
    #>  [95] segmented_2.1-4             S4Arrays_1.9.1
    #>  [97] viridisLite_0.4.2           dplyr_1.1.4
    #>  [99] gtable_0.3.6                sass_0.4.10
    #> [101] digest_0.6.37               BiocGenerics_0.55.1
    #> [103] SparseArray_1.9.1           ggrepel_0.9.6
    #> [105] htmlwidgets_1.6.4           farver_2.1.2
    #> [107] memoise_2.0.1               htmltools_0.5.8.1
    #> [109] lifecycle_1.0.4             h5mread_1.1.1
    #> [111] httr_1.4.7                  statmod_1.5.0
    #> [113] MASS_7.3-65