# Preprocessing

## Timothy Keyes

### 2025-06-04

    library(tidytof)
    library(dplyr)

## Preprocessing with tof_preprocess

Generally speaking, the raw ion counts measured for each analyte on a mass cytometer (the content of raw FCS files obtained directly from a mass cytometer) need to be transformed before CyTOF data analysis. Common preprocessing steps may include variance-stabilizing transformations - such as the hyperbolic arcsine (arcsinh) transformation or a log transformation - scaling/centering, and/or denoising.

To perform standard preprocessing tasks with `{tidytof}`, use `tof_preprocess`. `tof_preprocess`’s default behavior is to apply the arcsinh transformation (with a cofactor of 5) to each numeric column in the input `tof_tibble` as well as to remove the gaussian noise that Fluidigm software adds to each ion count (this noise is added for visualization purposes, but for most analyses, removing it is recommended).

As an example, we can preprocess `{tidytof}`’s built-in `phenograph_data` `tof_tibble` and see how our first few measurements change before and after.

    data(phenograph_data)

    # before preprocessing
    phenograph_data %>%
        select(cd45, cd34, cd38) %>%
        head()
    #> # A tibble: 6 × 3
    #>    cd45   cd34  cd38
    #>   <dbl>  <dbl> <dbl>
    #> 1  131.  3.23   1.51
    #> 2  230. -0.582 11.4
    #> 3  293.  5.20   1.84
    #> 4  431.  0.363 13.3
    #> # ℹ 2 more rows

    phenograph_data %>%
        # perform preprocessing
        tof_preprocess() %>%
        # inspect new values
        select(cd45, cd34, cd38) %>%
        head()
    #> # A tibble: 6 × 3
    #>    cd45    cd34  cd38
    #>   <dbl>   <dbl> <dbl>
    #> 1  3.96  0.608  0.298
    #> 2  4.52 -0.116  1.56
    #> 3  4.76  0.909  0.360
    #> 4  5.15  0.0725 1.70
    #> # ℹ 2 more rows

To alter `tof_preprocess`’s default behavior, change the `channel_cols` argument to specify which columns of `tof_tibble` should be transformed. Alter the `transform_fun` argument to specify a vector-valued function that should be used to transform each of the `channel_cols`. For example, suppose we want to center and scale each of our numeric columns instead of arcsinh-transforming them:

    phenograph_data %>%
        # preprocess
        tof_preprocess(transform_fun = scale) %>%
        # inspect new values
        select(cd45, cd34, cd38) %>%
        head()
    #> # A tibble: 6 × 3
    #>   cd45[,1] cd34[,1] cd38[,1]
    #>      <dbl>    <dbl>    <dbl>
    #> 1   -1.40     1.01   -0.437
    #> 2   -1.15    -0.911   0.0316
    #> 3   -0.999    2.00   -0.422
    #> 4   -0.661   -0.436   0.120
    #> # ℹ 2 more rows

To keep the gaussian noise added by Fluidigm software (or if you are working with a dataset that does not have this noise), set the `undo_noise` argument to `FALSE`.

## Postprocessing with tof_postprocess

As a final note, note that the built-in function `tof_postprocess` works nearly identically `tof_preprocess`, but provides different default behavior (namely, applying the reverse arcsinh transformation with a cofactor of 5 to all numeric columns. See `?tof_postprocess` for details).

    print(phenograph_data) %>%
        select(cd45, cd34, cd38) %>%
        head()
    #> # A tibble: 3,000 × 25
    #>   sample_name  phenograph_cluster    cd19 cd11b   cd34  cd45  cd123   cd33  cd47
    #>   <chr>        <chr>                <dbl> <dbl>  <dbl> <dbl>  <dbl>  <dbl> <dbl>
    #> 1 H1_PhenoGra… cluster1           -0.168  29.0   3.23   131. -0.609  1.21   13.0
    #> 2 H1_PhenoGra… cluster1            1.65    4.83 -0.582  230.  2.53  -0.507  12.9
    #> 3 H1_PhenoGra… cluster1            2.79   36.1   5.20   293. -0.265  3.67   27.1
    #> 4 H1_PhenoGra… cluster1            0.0816 48.8   0.363  431.  2.04   9.40   41.0
    #> # ℹ 2,996 more rows
    #> # ℹ 16 more variables: cd7 <dbl>, cd44 <dbl>, cd38 <dbl>, cd3 <dbl>,
    #> #   cd117 <dbl>, cd64 <dbl>, cd41 <dbl>, pstat3 <dbl>, pstat5 <dbl>,
    #> #   pampk <dbl>, p4ebp1 <dbl>, ps6 <dbl>, pcreb <dbl>, `pzap70-syk` <dbl>,
    #> #   prb <dbl>, `perk1-2` <dbl>
    #> # A tibble: 6 × 3
    #>    cd45   cd34  cd38
    #>   <dbl>  <dbl> <dbl>
    #> 1  131.  3.23   1.51
    #> 2  230. -0.582 11.4
    #> 3  293.  5.20   1.84
    #> 4  431.  0.363 13.3
    #> # ℹ 2 more rows

    # after preprocessing and post-processing, the data are the same
    # except that the re-added noise component is different for each value
    phenograph_data %>%
        tof_preprocess() %>%
        tof_postprocess(redo_noise = TRUE) %>%
        select(cd45, cd34, cd38) %>%
        head()
    #> # A tibble: 6 × 3
    #>    cd45   cd34  cd38
    #>   <dbl>  <dbl> <dbl>
    #> 1  131.  2.82   1.19
    #> 2  230. -1.38  10.7
    #> 3  293.  4.53   1.67
    #> 4  431. -0.280 12.4
    #> # ℹ 2 more rows

# Session info

    sessionInfo()
    #> R version 4.5.0 (2025-04-11 ucrt)
    #> Platform: x86_64-w64-mingw32/x64
    #> Running under: Windows Server 2022 x64 (build 20348)
    #>
    #> Matrix products: default
    #>   LAPACK version 3.12.1
    #>
    #> 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] tidyr_1.3.1                 stringr_1.5.1
    #>  [3] HDCytoData_1.29.0           flowCore_2.21.0
    #>  [5] SummarizedExperiment_1.39.0 Biobase_2.69.0
    #>  [7] GenomicRanges_1.61.0        GenomeInfoDb_1.45.4
    #>  [9] IRanges_2.43.0              S4Vectors_0.47.0
    #> [11] MatrixGenerics_1.21.0       matrixStats_1.5.0
    #> [13] ExperimentHub_2.99.5        AnnotationHub_3.99.5
    #> [15] BiocFileCache_2.99.5        dbplyr_2.5.0
    #> [17] BiocGenerics_0.55.0         generics_0.1.4
    #> [19] forcats_1.0.0               ggplot2_3.5.2
    #> [21] dplyr_1.1.4                 tidytof_1.3.0
    #>
    #> loaded via a namespace (and not attached):
    #>   [1] RColorBrewer_1.1-3   jsonlite_2.0.0       shape_1.4.6.1
    #>   [4] magrittr_2.0.3       farver_2.1.2         rmarkdown_2.29
    #>   [7] vctrs_0.6.5          memoise_2.0.1        sparsevctrs_0.3.4
    #>  [10] htmltools_0.5.8.1    S4Arrays_1.9.1       curl_6.2.3
    #>  [13] SparseArray_1.9.0    sass_0.4.10          parallelly_1.45.0
    #>  [16] bslib_0.9.0          httr2_1.1.2          lubridate_1.9.4
    #>  [19] cachem_1.1.0         commonmark_1.9.5     igraph_2.1.4
    #>  [22] mime_0.13            lifecycle_1.0.4      iterators_1.0.14
    #>  [25] pkgconfig_2.0.3      Matrix_1.7-3         R6_2.6.1
    #>  [28] fastmap_1.2.0        future_1.49.0        digest_0.6.37
    #>  [31] furrr_0.3.1          AnnotationDbi_1.71.0 irlba_2.3.5.1
    #>  [34] RSQLite_2.4.0        philentropy_0.9.0    labeling_0.4.3
    #>  [37] filelock_1.0.3       cytolib_2.21.0       yardstick_1.3.2
    #>  [40] timechange_0.3.0     httr_1.4.7           polyclip_1.10-7
    #>  [43] abind_1.4-8          compiler_4.5.0       bit64_4.6.0-1
    #>  [46] withr_3.0.2          doParallel_1.0.17    viridis_0.6.5
    #>  [49] DBI_1.2.3            hexbin_1.28.5        ggforce_0.4.2
    #>  [52] MASS_7.3-65          lava_1.8.1           embed_1.1.5
    #>  [55] rappdirs_0.3.3       DelayedArray_0.35.1  tools_4.5.0
    #>  [58] future.apply_1.11.3  nnet_7.3-20          glue_1.8.0
    #>  [61] grid_4.5.0           Rtsne_0.17           recipes_1.3.1
    #>  [64] gtable_0.3.6         tzdb_0.5.0           class_7.3-23
    #>  [67] rsample_1.3.0        data.table_1.17.4    hms_1.1.3
    #>  [70] utf8_1.2.5           tidygraph_1.3.1      XVector_0.49.0
    #>  [73] RcppAnnoy_0.0.22     markdown_2.0         ggrepel_0.9.6
    #>  [76] BiocVersion_3.22.0   foreach_1.5.2        pillar_1.10.2
    #>  [79] vroom_1.6.5          RcppHNSW_0.6.0       splines_4.5.0
    #>  [82] tweenr_2.0.3         lattice_0.22-7       survival_3.8-3
    #>  [85] bit_4.6.0            emdist_0.3-3         RProtoBufLib_2.21.0
    #>  [88] tidyselect_1.2.1     Biostrings_2.77.1    knitr_1.50
    #>  [91] gridExtra_2.3        litedown_0.7         xfun_0.52
    #>  [94] graphlayouts_1.2.2   hardhat_1.4.1        timeDate_4041.110
    #>  [97] stringi_1.8.7        UCSC.utils_1.5.0     yaml_2.3.10
    #> [100] evaluate_1.0.3       codetools_0.2-20     ggraph_2.2.1
    #> [103] tibble_3.2.1         BiocManager_1.30.25  cli_3.6.5
    #> [106] uwot_0.2.3           rpart_4.1.24         jquerylib_0.1.4
    #> [109] dichromat_2.0-0.1    Rcpp_1.0.14          globals_0.18.0
    #> [112] png_0.1-8            parallel_4.5.0       gower_1.0.2
    #> [115] readr_2.1.5          blob_1.2.4           listenv_0.9.1
    #> [118] glmnet_4.1-9         viridisLite_0.4.2    ipred_0.9-15
    #> [121] ggridges_0.5.6       scales_1.4.0         prodlim_2025.04.28
    #> [124] purrr_1.0.4          crayon_1.5.3         rlang_1.1.6
    #> [127] KEGGREST_1.49.0