Date: 2026-05-18

# Getting started with ‘SpotSweeper’

Michael Totty1, Boyi Guo1 and Stephanie Hicks1

1Johns Hopkins Bloomberg School of Public Health, Baltimore, MD, USA

#### 2026-05-18

# Contents

## 1 Introduction

`SpotSweeper` is an R package for spatial transcriptomics data quality control (QC). It provides functions for detecting and visualizing spot-level local outliers and artifacts using spatially-aware methods. The package is designed to work with SpatialExperiment objects, and is compatible with data from 10X Genomics Visium and other spatial transcriptomics platforms.

## 2 Installation

Currently, the only way to install `SpotSweeper` is by downloading the development version which can be installed from GitHub using the following:

    if (!require("devtools")) install.packages("devtools")
    remotes::install_github("MicTott/SpotSweeper")

Once accepted in Bioconductor, `SpotSweeper` will be installable using:

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

    BiocManager::install("SpotSweeper")

## 3 Spot-level local outlier detection

### 3.1 Loading example data

Here we’ll walk you through the standard workflow for using ‘SpotSweeper’ to detect and visualize local outliers in spatial transcriptomics data. We’ll use the `Visium_humanDLPFC` dataset from the `STexampleData` package, which is a `SpatialExperiment` object.

Because local outliers will be saved in the `colData` of the `SpatialExperiment` object, we’ll first view the `colData` and drop out-of-tissue spots before calculating quality control (QC) metrics and running `SpotSweeper`.

    library(SpotSweeper)

    # load  Maynard et al DLPFC daatset
    spe <- STexampleData::Visium_humanDLPFC()

    ## see ?STexampleData and browseVignettes('STexampleData') for documentation

    ## loading from cache

    # show column data before SpotSweeper
    colnames(colData(spe))

    ## [1] "barcode_id"   "sample_id"    "in_tissue"    "array_row"    "array_col"
    ## [6] "ground_truth" "reference"    "cell_count"

    # drop out-of-tissue spots
    spe <- spe[, spe$in_tissue == 1]

### 3.2 Calculating QC metrics using `scuttle`

We’ll use the `scuttle` package to calculate QC metrics. To do this, we’ll need to first change the `rownames` from gene id to gene names. We’ll then get the mitochondrial transcripts and calculate QC metrics for each spot using `scuttle::addPerCellQCMetrics`.

    # change from gene id to gene names
    rownames(spe) <- rowData(spe)$gene_name

    # identifying the mitochondrial transcripts
    is.mito <- rownames(spe)[grepl("^MT-", rownames(spe))]

    # calculating QC metrics for each spot using scuttle
    spe <- scuttle::addPerCellQCMetrics(spe, subsets = list(Mito = is.mito))

    ## Warning in scuttle::addPerCellQCMetrics(spe, subsets = list(Mito = is.mito)): 'scuttle::addPerCellQCMetrics' is deprecated.
    ## Use 'scrapper::quickRnaQc.se' instead.
    ## See help("Deprecated")

    ## Warning in .per_cell_qc_metrics(assay(x, assay.type), subsets = subsets, : 'perCellQCMetrics' is deprecated.
    ## Use 'scrapper::computeRnaQcMetrics' instead.
    ## See help("Deprecated")

    ## using unknown matrix fallback for 'dgTMatrix'

    colnames(colData(spe))

    ##  [1] "barcode_id"            "sample_id"             "in_tissue"
    ##  [4] "array_row"             "array_col"             "ground_truth"
    ##  [7] "reference"             "cell_count"            "sum"
    ## [10] "detected"              "subsets_Mito_sum"      "subsets_Mito_detected"
    ## [13] "subsets_Mito_percent"  "total"

### 3.3 Identifying local outliers using `SpotSweeper`

We can now use `SpotSweeper` to identify local outliers in the spatial transcriptomics data. We’ll use the `localOutliers` function to detect local outliers based on the unique detected genes, total library size, and percent of the total reads that are mitochondrial. These methods assume a normal distribution, so we’ll use the log-transformed sum of the counts and the log-transformed number of detected genes. For mitochondrial percent, we’ll use the raw mitochondrial percentage.

    # library size
    spe <- localOutliers(spe,
        metric = "sum",
        direction = "lower",
        log = TRUE
    )

    # unique genes
    spe <- localOutliers(spe,
        metric = "detected",
        direction = "lower",
        log = TRUE
    )

    # mitochondrial percent
    spe <- localOutliers(spe,
        metric = "subsets_Mito_percent",
        direction = "higher",
        log = FALSE
    )

The `localOutlier` function automatically outputs the results to the `colData` with the naming convention `X_outliers`, where `X` is the name of the input `colData`. We can then combine all outliers into a single column called `local_outliers` in the `colData` of the `SpatialExperiment` object.

    # combine all outliers into "local_outliers" column
    spe$local_outliers <- as.logical(spe$sum_outliers) |
        as.logical(spe$detected_outliers) |
        as.logical(spe$subsets_Mito_percent_outliers)

### 3.4 Visualizing local outliers

We can visualize the local outliers using the `plotQCmetrics` function. This function creates a scatter plot of the specified metric and highlights the local outliers in red using the `escheR` package. Here, we’ll visualize local outliers of library size, unique genes, mitochondrial percent, and finally, all local outliers. We’ll then arrange these plots in a grid using `ggpubr::arrange`.

    library(escheR)

    ## Loading required package: ggplot2

    # all local outliers
    plotQCmetrics(spe, metric = "sum_log", outliers = "local_outliers", point_size = 1.1,
           stroke = 0.75) +
          ggtitle("All Local Outliers")

## 4 Removing technical artifacts using `SpotSweeper`

### 4.1 Loading example data

    # load in DLPFC sample with hangnail artifact
    data(DLPFC_artifact)
    spe <- DLPFC_artifact

    # inspect colData before artifact detection
    colnames(colData(spe))

    ##  [1] "sample_id"          "in_tissue"          "array_row"
    ##  [4] "array_col"          "key"                "sum_umi"
    ##  [7] "sum_gene"           "expr_chrM"          "expr_chrM_ratio"
    ## [10] "ManualAnnotation"   "subject"            "region"
    ## [13] "sex"                "age"                "diagnosis"
    ## [16] "sample_id_complete" "count"              "sizeFactor"

### 4.2 Visualizing technical artifacts

Technical artifacts can commonly be visualized by standard QC metrics, including library size, unique genes, or mitochondrial percentage. We can first visualize the technical artifacts using the `plotQCmetrics` function. In this sample, we can clearly see a hangnail artifact on the right side of the tissue section in the mitochondrial ratio plot.

    plotQCmetrics(spe,
        metric = "expr_chrM_ratio",
        outliers = NULL, point_size = 1.1
    ) +
        ggtitle("Mitochondrial Percent")

### 4.3 Identifying artifacts using `SpotSweeper`

We can then use the `findArtifacts` function to identify artifacts in the spatial transcriptomics (data. This function identifies technical artifacts based on the first principle component of the local variance of the specified QC metric (`mito_percent`) at numerous neighorhood sizes (`n_order=5`). Currently, `kmeans` clustering is used to cluster the technical artifact vs high-quality Visium spots. Similar to `localOutliers`, the `findArtifacts` function then outputs the results to the `colData`.

    # find artifacts using SpotSweeper
    spe <- findArtifacts(spe,
        mito_percent = "expr_chrM_ratio",
        mito_sum = "expr_chrM",
        n_order = 5,
        name = "artifact"
    )

    # check that "artifact" is now in colData
    colnames(colData(spe))

    ##  [1] "sample_id"           "in_tissue"           "array_row"
    ##  [4] "array_col"           "key"                 "sum_umi"
    ##  [7] "sum_gene"            "expr_chrM"           "expr_chrM_ratio"
    ## [10] "ManualAnnotation"    "subject"             "region"
    ## [13] "sex"                 "age"                 "diagnosis"
    ## [16] "sample_id_complete"  "count"               "sizeFactor"
    ## [19] "expr_chrM_ratio_log" "coords"              "k6"
    ## [22] "k18"                 "k36"                 "k60"
    ## [25] "k90"                 "artifact"

### 4.4 Visualizing artifacts

We can visualize the artifacts using the `escheR` package. Here, we’ll visualize the artifacts using the `plotQCmetrics` function and arrange these plots using `ggpubr::arrange`.

    plotQCmetrics(spe,
        metric = "expr_chrM_ratio",
        outliers = "artifact", point_size = 1.1
    ) +
        ggtitle("Hangnail artifact")

# Session information

    utils::sessionInfo()

    ## R version 4.6.0 Patched (2026-05-01 r89994)
    ## Platform: aarch64-apple-darwin23
    ## Running under: macOS Tahoe 26.3.1
    ##
    ## Matrix products: default
    ## BLAS:   /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRblas.0.dylib
    ## LAPACK: /Library/Frameworks/R.framework/Versions/4.6/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] stats4    stats     graphics  grDevices utils     datasets  methods
    ## [8] base
    ##
    ## other attached packages:
    ##  [1] escheR_1.13.0               ggplot2_4.0.3
    ##  [3] STexampleData_1.21.0        SpatialExperiment_1.23.0
    ##  [5] SingleCellExperiment_1.35.1 SummarizedExperiment_1.43.0
    ##  [7] Biobase_2.73.1              GenomicRanges_1.65.0
    ##  [9] Seqinfo_1.3.0               IRanges_2.47.1
    ## [11] S4Vectors_0.51.2            MatrixGenerics_1.25.0
    ## [13] matrixStats_1.5.0           ExperimentHub_3.3.0
    ## [15] AnnotationHub_4.3.0         BiocFileCache_3.3.0
    ## [17] dbplyr_2.5.2                BiocGenerics_0.59.2
    ## [19] generics_0.1.4              SpotSweeper_1.9.0
    ## [21] BiocStyle_2.41.0
    ##
    ## loaded via a namespace (and not attached):
    ##  [1] tidyselect_1.2.1     viridisLite_0.4.3    dplyr_1.2.1
    ##  [4] farver_2.1.2         blob_1.3.0           Biostrings_2.81.1
    ##  [7] filelock_1.0.3       S7_0.2.2             fastmap_1.2.0
    ## [10] digest_0.6.39        lifecycle_1.0.5      KEGGREST_1.53.0
    ## [13] terra_1.9-27         RSQLite_3.52.0       magrittr_2.0.5
    ## [16] compiler_4.6.0       rlang_1.2.0          sass_0.4.10
    ## [19] tools_4.6.0          yaml_2.3.12          knitr_1.51
    ## [22] labeling_0.4.3       S4Arrays_1.13.0      bit_4.6.0
    ## [25] curl_7.1.0           DelayedArray_0.39.2  RColorBrewer_1.1-3
    ## [28] abind_1.4-8          BiocParallel_1.47.0  withr_3.0.2
    ## [31] purrr_1.2.2          grid_4.6.0           beachmat_2.29.0
    ## [34] scales_1.4.0         MASS_7.3-65          tinytex_0.59
    ## [37] dichromat_2.0-0.1    cli_3.6.6            crayon_1.5.3
    ## [40] rmarkdown_2.31       otel_0.2.0           httr_1.4.8
    ## [43] rjson_0.2.23         scuttle_1.23.1       DBI_1.3.0
    ## [46] cachem_1.1.0         parallel_4.6.0       AnnotationDbi_1.75.0
    ## [49] BiocManager_1.30.27  XVector_0.53.0       vctrs_0.7.3
    ## [52] Matrix_1.7-5         jsonlite_2.0.0       bookdown_0.46
    ## [55] BiocNeighbors_2.7.2  bit64_4.8.0          magick_2.9.1
    ## [58] jquerylib_0.1.4      glue_1.8.1           codetools_0.2-20
    ## [61] gtable_0.3.6         BiocVersion_3.24.0   tibble_3.3.1
    ## [64] pillar_1.11.1        rappdirs_0.3.4       htmltools_0.5.9
    ## [67] R6_2.6.1             httr2_1.2.2          evaluate_1.0.5
    ## [70] lattice_0.22-9       png_0.1-9            memoise_2.0.1
    ## [73] bslib_0.11.0         Rcpp_1.1.1-1.1       SparseArray_1.13.2
    ## [76] xfun_0.57            spatialEco_2.0-4     pkgconfig_2.0.3