Often, clustering single-cell data to identify communities of cells with shared characteristics is a major goal of high-dimensional cytometry data analysis.
To do this, {tidytof} provides the
tof_cluster() verb. Several clustering methods are
implemented in {tidytof}, including the following:
Each of these methods are wrapped by tof_cluster().
tof_cluster()To demonstrate, we can apply the PhenoGraph clustering algorithm to
{tidytof}’s built-in phenograph_data. Note
that phenograph_data contains 3000 total cells (1000 each
from 3 clusters identified in the original PhenoGraph
publication). For demonstration purposes, we also metacluster our
PhenoGraph clusters using k-means clustering.
data(phenograph_data)
set.seed(203L)
phenograph_clusters <-
phenograph_data |>
tof_preprocess() |>
tof_cluster(
cluster_cols = starts_with("cd"),
num_neighbors = 50L,
distance_function = "cosine",
method = "phenograph"
) |>
tof_metacluster(
cluster_col = .phenograph_cluster,
metacluster_cols = starts_with("cd"),
num_metaclusters = 3L,
method = "kmeans"
)
phenograph_clusters |>
dplyr::select(sample_name, .phenograph_cluster, .kmeans_metacluster) |>
head()
#> # A tibble: 6 × 3
#> sample_name .phenograph_cluster .kmeans_metacluster
#> <chr> <chr> <chr>
#> 1 H1_PhenoGraph_cluster1 5 2
#> 2 H1_PhenoGraph_cluster1 1 2
#> 3 H1_PhenoGraph_cluster1 5 2
#> 4 H1_PhenoGraph_cluster1 1 2
#> 5 H1_PhenoGraph_cluster1 1 2
#> 6 H1_PhenoGraph_cluster1 5 2The outputs of both tof_cluster() and
tof_metacluster() are a tof_tbl identical to
the input tibble, but now with the addition of an additional column (in
this case, “.phenograph_cluster” and “.kmeans_metacluster”) that encodes
the cluster id for each cell in the input tof_tbl. Note
that all output columns added to a tibble or tof_tbl by
{tidytof} begin with a full-stop (“.”) to reduce the
likelihood of collisions with existing column names.
Because the output of tof_cluster is a
tof_tbl, we can use dplyr’s count
method to assess the accuracy of our clustering procedure compared to
the original clustering from the PhenoGraph paper.
phenograph_clusters |>
dplyr::count(phenograph_cluster, .kmeans_metacluster, sort = TRUE)
#> # A tibble: 4 × 3
#> phenograph_cluster .kmeans_metacluster n
#> <chr> <chr> <int>
#> 1 cluster2 1 1000
#> 2 cluster3 3 1000
#> 3 cluster1 2 995
#> 4 cluster1 3 5Here, we can see that our clustering procedure groups most cells from the same PhenoGraph cluster with one another (with a small number of mistakes).
To change which clustering algorithm tof_cluster uses,
alter the method flag.
# use the kmeans algorithm
phenograph_data |>
tof_preprocess() |>
tof_cluster(
cluster_cols = contains("cd"),
method = "kmeans"
)
# use the flowsom algorithm
phenograph_data |>
tof_preprocess() |>
tof_cluster(
cluster_cols = contains("cd"),
method = "flowsom"
)To change the columns used to compute the clusters, change the
cluster_cols flag. And finally, if you want to return a
one-column tibble that only includes the cluster labels (as
opposed to the cluster labels added as a new column to the input
tof_tbl), set augment to
FALSE.
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 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.32.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] HDCytoData_1.33.1 flowCore_2.25.1
#> [3] SummarizedExperiment_1.43.0 Biobase_2.73.2
#> [5] GenomicRanges_1.65.4 Seqinfo_1.3.2
#> [7] IRanges_2.47.5 S4Vectors_0.51.10
#> [9] MatrixGenerics_1.25.0 matrixStats_1.5.0
#> [11] ExperimentHub_3.3.2 AnnotationHub_4.3.2
#> [13] BiocFileCache_3.3.0 dbplyr_2.6.0
#> [15] BiocGenerics_0.59.12 generics_0.1.4
#> [17] forcats_1.0.1 ggplot2_4.0.3
#> [19] dplyr_1.2.1 tidytof_1.7.0
#> [21] rmarkdown_2.32
#>
#> loaded via a namespace (and not attached):
#> [1] RColorBrewer_1.1-3 sys_3.4.3 jsonlite_2.0.0
#> [4] shape_1.4.6.1 magrittr_2.0.5 farver_2.1.2
#> [7] vctrs_0.7.3 memoise_2.0.1 sparsevctrs_0.3.6
#> [10] BiocBaseUtils_1.15.1 htmltools_0.5.9 S4Arrays_1.13.2
#> [13] curl_8.0.0 SparseArray_1.13.4 sass_0.4.10
#> [16] parallelly_1.48.0 bslib_0.12.0 httr2_1.3.0
#> [19] lubridate_1.9.5 cachem_1.1.0 buildtools_1.0.0
#> [22] igraph_2.3.4 lifecycle_1.0.5 iterators_1.0.14
#> [25] pkgconfig_2.0.3 Matrix_1.7-6 R6_2.6.1
#> [28] fastmap_1.2.0 future_1.76.0 digest_0.6.39
#> [31] AnnotationDbi_1.75.2 RSQLite_3.53.3 labeling_0.4.3
#> [34] filelock_1.0.3 cytolib_2.25.0 yardstick_1.4.0
#> [37] timechange_0.4.0 httr_1.4.9 polyclip_1.10-7
#> [40] abind_1.4-8 compiler_4.6.1 bit64_4.8.6
#> [43] withr_3.0.3 doParallel_1.0.17 S7_0.2.2
#> [46] viridis_0.6.5 DBI_1.3.0 ggforce_0.5.0
#> [49] MASS_7.3-66 lava_1.9.3 rappdirs_0.3.4
#> [52] DelayedArray_0.39.8 tools_4.6.1 otel_0.2.0
#> [55] future.apply_1.20.2 nnet_7.3-21 glue_1.8.1
#> [58] grid_4.6.1 recipes_1.4.0 gtable_0.3.6
#> [61] tzdb_0.5.0 class_7.3-24 tidyr_1.3.2
#> [64] data.table_1.18.6.1 hms_1.1.4 utf8_1.2.6
#> [67] tidygraph_1.3.1 XVector_0.53.0 ggrepel_0.9.8
#> [70] BiocVersion_3.24.0 foreach_1.5.2 pillar_1.11.1
#> [73] stringr_1.6.0 RcppHNSW_0.7.0 splines_4.6.1
#> [76] tweenr_2.0.3 lattice_0.23-1 survival_3.8-12
#> [79] bit_4.6.0 RProtoBufLib_2.25.0 tidyselect_1.2.1
#> [82] maketools_1.3.2 Biostrings_2.81.9 knitr_1.52
#> [85] gridExtra_2.3.1 xfun_0.61 graphlayouts_1.2.5
#> [88] hardhat_1.4.3 timeDate_4052.112 stringi_1.8.9
#> [91] yaml_2.3.12 evaluate_1.0.5 codetools_0.2-20
#> [94] ggraph_2.2.2 tibble_3.3.1 BiocManager_1.30.27
#> [97] cli_3.6.6 rpart_4.1.27 jquerylib_0.1.4
#> [100] Rcpp_1.1.2 globals_0.19.1 png_0.1-9
#> [103] parallel_4.6.1 gower_1.0.2 readr_2.2.0
#> [106] blob_1.3.0 listenv_1.1.0 glmnet_5.1
#> [109] viridisLite_0.4.3 ipred_0.9-16 ggridges_0.5.7
#> [112] scales_1.4.0 prodlim_2026.03.11 crayon_1.5.3
#> [115] purrr_1.2.2 rlang_1.3.0 KEGGREST_1.53.6