Designing and evaluating a spatial multi-omics workflow

Analysis questions determine the workflow

A comprehensive report should expose the consequences of analytical choices. Its purpose is not to produce an attractive embedding or maximize the number of significant features. The workflow follows the analysis chain of the historical mouse-brain vignette: inspect measurements and correspondence; compare individual, spatial and integrated representations; characterize regions; examine paired features; then connect measured identities to pathways and networks.

Question Native interface Evidence to inspect
What was measured and retained? normalizeSMData(), native containers and image access Selected layer, QC exclusions, scale and acquisition provenance
What changes when spatial information is added? runMetabolicPCA(), scaleSMData(), runSpatialGraphPCA() Same selected features and all retained observations in both representations
Does integration improve the representation? multiOmicIntegration(), getKmeanClusters() Individual versus joint structure; external-reference ARI, approximate silhouettes and sensitivity across K
Which features characterize a region? findAllDEMs(method="markers"), demsHeatmap() Pairwise AUC, Cohen effects, detection, mean expression and spatial-block sensitivity
Which differences reproduce across biological samples? findAllDEMs(method="replicate") Explicit design, replicate means, effect intervals and the complete BH family
Is an association explained by regional composition? findCorrelatedFeatures(), plot3DFeature() Raw versus covariate-residual correlations and matched spatial layers
Which members support a pathway? buildPathwayIndex(), createPathwayAssay(), findRegionalPathways() Measured coverage, ambiguity exclusions, complete ranks and leading edges
Are pathway members coordinated and where are they? runRAMPGeseca(), plotRegionalPathways(), pathwayNetworkPlots() The same identity assay and membership index in every downstream view

The first examples use synthetic teaching data, not biological findings. Later sections execute the complete registered human-brain, FMP10/Visium and DHB/Visium inputs, without downsampling observations for analysis. Building this article downloads versioned resources when no verified cache is available and can take tens of minutes. The installed workflows/prepare_mouse_brain.R and workflows/run_mouse_brain.R scripts also support offline command-line use.

Supply modalities and study design explicitly

x <- SpaMTPData::spaMTPExampleData(paired = TRUE)
protein <- rbind(marker1 = c(1:6, 8:13), marker2 = c(6:1, 2:7),
                 marker3 = rep(c(2, 7, 4), 4))
colnames(protein) <- colnames(x)
SingleCellExperiment::altExp(x, "protein") <-
    SingleCellExperiment::SingleCellExperiment(assays = list(counts = protein))
spec <- list(
    main = list(type = "metabolomics", layer = "counts"),
    transcriptome = list(type = "transcriptomics", layer = "counts"),
    protein = list(type = "proteomics", layer = "counts")
)

The workflow preserves the supplied layers. Its declared baseline uses native library-size normalization followed by conversion to log2 units for raw MS/RNA counts, and log2(1 + value) for protein counts. For already transformed data, choose normalization="none". This baseline does not replace vendor-specific spectral processing, RNA count QC or a platform-appropriate normalization design. In particular, logged values must not be logged again or labelled raw counts.

Biological replication is an explicit metadata field. A pixel, technical pool, spatial block, image channel or integration cluster does not create an independent specimen. Transcriptomics, metabolomics and proteomics can be declared as three modalities; H&E, DAPI and RGB channels remain contextual images unless their measurements support a separate stated assay model.

Compare representations using the same observations and features

fit <- runSpaMTPWorkflow(x, spec, regions = "region", clusters = 2,
    npcs = 2, association_features = 3,
    coordinate_units = "synthetic grid units",
    structure = list(primary = "spatial", k_grid = c(2, 3),
                     reference = "region", umap = FALSE,
                     stability_blocks = 3))
fit$structure$metrics
##               representation k approx_silhouette reference_ARI
## 1                 main / PCA 2         0.3927081    0.66481994
## 2                 main / PCA 3         0.4015610    0.47323944
## 3           main / Graph PCA 2         0.5855174    1.00000000
## 4           main / Graph PCA 3         0.6789031    0.84507042
## 5        transcriptome / PCA 2         0.3706615   -0.04336043
## 6        transcriptome / PCA 3         0.3584979   -0.04011461
## 7  transcriptome / Graph PCA 2         0.4607921   -0.05319149
## 8  transcriptome / Graph PCA 3         0.5043638   -0.04904632
## 9              protein / PCA 2         0.3304443    0.17728532
## 10             protein / PCA 3         0.4181479    0.27507163
## 11       protein / Graph PCA 2         0.5465039    1.00000000
## 12       protein / Graph PCA 3         0.5779831    0.84507042
## 13               Joint / PCA 2         0.1883832   -0.10000000
## 14               Joint / PCA 3         0.2316406    0.24137931
## 15         Joint / Graph PCA 2         0.2916312    0.66481994
## 16         Joint / Graph PCA 3         0.3836235    0.53521127
##    conditional_stability stability_omissions spatial_neighbor_agreement
## 1              1.0000000                   3                  0.7037037
## 2              0.6428571                   3                  0.5185185
## 3              1.0000000                   3                  0.8148148
## 4              0.6534653                   3                  0.7037037
## 5              0.1600000                   3                  0.6111111
## 6              0.5098039                   3                  0.2407407
## 7              1.0000000                   3                  0.7222222
## 8              1.0000000                   3                  0.6111111
## 9              0.2857143                   3                  0.5000000
## 10             0.6298472                   3                  0.4259259
## 11             1.0000000                   3                  0.8148148
## 12             1.0000000                   3                  0.7037037
## 13             0.2800000                   3                  0.3888889
## 14             0.5087719                   3                  0.3333333
## 15             0.1384615                   3                  0.7592593
## 16             0.5000000                   3                  0.6481481
##    smallest_cluster
## 1                 5
## 2                 2
## 3                 6
## 4                 1
## 5                 3
## 6                 3
## 7                 2
## 8                 1
## 9                 5
## 10                3
## 11                6
## 12                1
## 13                6
## 14                4
## 15                5
## 16                1

runMetabolicPCA(features=...) now records all selected features explicitly. Ordinary and graph PCA use the same variable features, centred and scaled in the same way. Graph PCA uses a sparse Laplacian for every retained observation and keeps different samples in separate spatial graphs. It is not fitted on a small report preview and then discarded: graph embeddings feed the spatial joint representation and can be selected as the downstream primary representation.

The joint representation uses multiOmicIntegration() to concatenate standardized modality embeddings. This native implementation is not Seurat WNN. Recovering and benchmarking WNN requires a separate, validated method implementation. UMAP is a visualization of a representation; clusters and metrics use its complete retained dimensions. A nicer-looking UMAP is not a performance test.

The number of clusters and primary method are chosen before examining reference ARI. Approximate silhouettes assess separation, while ARI compares to supplied external annotation. Spatial-neighbor agreement is a smoothness diagnostic; spatial regularization increases it by construction. Spatial-block omission refits clustering conditional on full-data embeddings. It does not validate feature selection, normalization or generalization to new specimens.

Region characterization is richer than a mean difference

markers <- fit$region_analysis$modalities$main$markers
head(markers$DEMs[, c("gene", "cluster", "logFC", "mean_auc",
                     "min_auc", "mean_cohen", "direction_stability")])
##    gene cluster      logFC   mean_auc    min_auc mean_cohen direction_stability
## 1 peak1    core -0.4483818 0.27777778 0.27777778 -0.7415451                   1
## 2 peak2    core -0.5567565 0.22222222 0.22222222 -1.0026087                   1
## 3 peak3    core -1.0282257 0.08333333 0.08333333 -1.6161042                   1
## 4 peak4    core  0.6046004 0.66666667 0.66666667  0.8884857                   1
## 5 peak5    core  0.9645023 0.88888889 0.88888889  1.5201261                   1
## 6 peak6    core  0.9982865 0.91666667 0.91666667  1.4499620                   1
demsHeatmap(markers, n = 2, only.pos = TRUE, logfc.threshold = 0,
    order.by = "mean_auc", fontsize_row = 9, fontsize_col = 10)

The native marker mode delegates pairwise scoring to scran::scoreMarkers(). AUC measures pairwise concordance, including half credit for ties. Mean and minimum AUC distinguish broadly specific markers from features that separate only some regions. Cohen effects account for within-region variability. Summaries equally weight other eligible regions so that the largest anatomical compartment does not automatically dominate the reference. Detection fractions refer to non-zero values on the explicitly selected layer; residualized layers need different interpretation.

Coordinate-only blocks support a leave-one-block-out sensitivity analysis. The result records the range of Cohen effects, marker-rank changes, direction agreement and omitted fits that were ineligible because a region became too small. These ranges are not confidence intervals. Region labels are held fixed, including when learned from these data, and no pixel-based P values are manufactured.

demsHeatmap() uses the analysis expression matrix directly. Its FDR ordering now sorts numerical FDR; it no longer sorts a regulation label or applies CPM to an already transformed marker matrix. The heatmap, detection plot, spatial feature maps and interactive explorer use the same ranked feature identifiers.

Keep biological-replicate inference separate

The published brain sections do not provide independent biological replicates. This design example therefore explicitly simulates four paired patients, each with three observations per condition; it is not an analysis of the real sections.

design <- expand.grid(pixel = seq_len(3), condition = c("control", "treated"),
                      patient_id = paste0("simulated_patient", seq_len(4)))
set.seed(1234)
expression <- matrix(rnorm(6 * nrow(design), mean = 5, sd = 0.3), nrow = 6,
    dimnames = list(paste0("feature", seq_len(6)),
                    paste0("observation", seq_len(nrow(design)))))
expression[1:2, design$condition == "treated"] <-
    expression[1:2, design$condition == "treated"] + 1.2
replicate_example <- SingleCellExperiment::SingleCellExperiment(
    assays = list(logcounts = expression), colData = S4Vectors::DataFrame(design))
replicate_de <- findAllDEMs(replicate_example, ident = "condition", assay = "main",
    slot = "logcounts", method = "replicate", replicate = "patient_id",
    contrasts = list(treatment_control = list(
        numerator = "treated", denominator = "control", paired = TRUE)))
stopifnot(identical(replicate_de$tests$treatment_control$status, "completed"))
head(replicate_de$tests$treatment_control$table)
##        logFC       CI.L      CI.R  AveExpr         t      P.Value    adj.P.Val
## 1 1.25278467  0.8678480 1.6377213 5.496658 6.8374968 2.124161e-06 6.372483e-06
## 2 1.33581582  0.9516022 1.7200295 5.635477 7.3043865 8.730057e-07 5.238034e-06
## 3 0.01823587 -0.3016203 0.3380921 5.045249 0.1197793 9.059848e-01 9.059848e-01
## 4 0.18103195 -0.1034775 0.4655415 4.945062 1.3368060 1.979376e-01 3.958752e-01
## 5 0.06925296 -0.2216750 0.3601809 4.962307 0.5001068 6.230587e-01 7.476705e-01
## 6 0.06732186 -0.1952123 0.3298560 4.885462 0.5387413 5.966660e-01 7.476705e-01
##           B  feature
## 1 16.211052 feature1
## 2 19.483081 feature2
## 3 -6.949913 feature3
## 4 -6.071433 feature4
## 5 -6.833080 feature5
## 6 -6.813190 feature6

This mode fits limma to equally weighted biological-replicate means of the selected values. Paired contrasts retain complete pairs; at least three complete pairs (or three independent replicates per arm) are required. The result retains the design, coefficient, residual degrees of freedom, replicate expression matrix, pointwise moderated intervals, and every tested feature. BH correction is within modality and contrast. A logFC column is a difference on the selected workflow scale; it is not automatically a fold change in raw abundance. RNA count models may instead require independently designed count pseudobulking and edgeR/DESeq2.

The historical random-pool mode remains available for compatibility and emits a warning about technical partitions. It is never used by the automatic workflow.

Ask whether colocalization survives regional composition

head(fit$associations[[1]])
##    feature1 feature2 correlation correlation_raw modality1     modality2
## 1     peak6    gene2   0.7618268       0.6014781      main transcriptome
## 6     peak3    gene1  -0.6226655      -0.5516854      main transcriptome
## 7     peak3    gene3   0.4880833       0.3666491      main transcriptome
## 8     peak3    gene2   0.2416135       0.1756689      main transcriptome
## 11    peak5    gene2   0.2343414       0.1851459      main transcriptome
## 2     peak6    gene4  -0.2282004      -0.2013184      main transcriptome
##    observations
## 1            12
## 6            12
## 7            12
## 8            12
## 11           12
## 2            12

findCorrelatedFeatures(covariates=...) retains raw Pearson correlation and the correlation between both features after regression on the declared sample, replicate and region fields. The workflow queries regional marker anchors against all PCA-selected target features. It records those selection sets and the matched observations. Strong raw association can disappear after removing a common regional shift. A residual association can still reflect spatial autocorrelation or other unmeasured structure, and selected pairs require independent validation.

Exact feature identifiers work for RNA and protein as well as metabolites; no artificial m/z metadata is needed. The report includes paired scatter plots and native plot3DFeature() views of the same selected pairs.

Map identities before pathway statistics

Here the complete human-brain resource supplies a real measured panel and its archived cout_labels cell labels. These are descriptive groups from a single section, not independent replicates or newly validated anatomical boundaries.

database <- loadSpaMTPDatabase(
    c("source_df", "analyte", "analytehaspathway", "pathway"), version = "3.0.7")
gene_reference <- SpaMTPdb::spaMTPdbGeneReference(version = "2026.7.7")
gene_index <- buildGeneMappingIndex(database$source_df,
                                  gene_reference = gene_reference)
index <- buildPathwayIndex(database, gene_index = gene_index)
real_data <- SpaMTPData::spaMTPData("human_brain_integrated", version = "1.1.0")
real_data <- normalizeSMData(real_data, "LogNormalize", assay = "SPT", verbose = FALSE)
identity <- createPathwayAssay(real_data, analyte_type = "genes",
    assay = "SPT", slot = "logcounts", new_assay = "gene_ids",
    pathway_index = index, organism = "Homo sapiens")
## Warning: 10 gene input(s) unresolved; inspect mapping status and
## candidate_gene_ids.
identity_markers <- findAllDEMs(identity, ident = "cout_labels", assay = "gene_ids",
    slot = "logcounts", method = "markers", spatial_blocks = 0)
# Build a feature-by-region matrix of the complete identity-level effects.
ranks <- identity_markers$expression
for (region in colnames(ranks)) {
    tab <- identity_markers$DEMs[identity_markers$DEMs$cluster == region, ]
    ranks[, region] <- tab$logFC[match(rownames(ranks), tab$gene)]
}
set.seed(1234)
regional <- findRegionalPathways(identity, ident = "cout_labels",
    ranks = list(genes = ranks), pathway_index = index,
    min_path_size = 5, max_path_size = 500)
##   |                                                                              |                                                                      |   0%  |                                                                              |====                                                                  |   6%  |                                                                              |=========                                                             |  12%  |                                                                              |=============                                                         |  19%  |                                                                              |==================                                                    |  25%  |                                                                              |======================                                                |  31%  |                                                                              |==========================                                            |  38%  |                                                                              |===============================                                       |  44%  |                                                                              |===================================                                   |  50%  |                                                                              |=======================================                               |  56%  |                                                                              |============================================                          |  62%  |                                                                              |================================================                      |  69%  |                                                                              |====================================================                  |  75%  |                                                                              |=========================================================             |  81%  |                                                                              |=============================================================         |  88%  |                                                                              |==================================================================    |  94%  |                                                                              |======================================================================| 100%
## Warning in fgsea::fgseaMultilevel(pathways = sets, stats =
## used_ranks[[region]], : There were 3 pathways for which P-values were not
## calculated properly due to unbalanced (positive and negative) gene-level
## statistic values. For such pathways pval, padj, NES, log2err are set to NA. You
## can try to increase the value of the argument nPermSimple (for example set it
## nPermSimple = 10000)
##   |                                                                              |                                                                      |   0%  |                                                                              |====                                                                  |   6%  |                                                                              |=========                                                             |  12%  |                                                                              |=============                                                         |  19%  |                                                                              |==================                                                    |  25%  |                                                                              |======================                                                |  31%  |                                                                              |==========================                                            |  38%  |                                                                              |===============================                                       |  44%  |                                                                              |===================================                                   |  50%  |                                                                              |=======================================                               |  56%  |                                                                              |============================================                          |  62%  |                                                                              |================================================                      |  69%  |                                                                              |====================================================                  |  75%  |                                                                              |=========================================================             |  81%  |                                                                              |=============================================================         |  88%  |                                                                              |==================================================================    |  94%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |======                                                                |   9%  |                                                                              |=============                                                         |  18%  |                                                                              |===================                                                   |  27%  |                                                                              |=========================                                             |  36%  |                                                                              |================================                                      |  45%  |                                                                              |======================================                                |  55%  |                                                                              |=============================================                         |  64%  |                                                                              |===================================================                   |  73%  |                                                                              |=========================================================             |  82%  |                                                                              |================================================================      |  91%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |=======                                                               |  10%  |                                                                              |==============                                                        |  20%  |                                                                              |=====================                                                 |  30%  |                                                                              |============================                                          |  40%  |                                                                              |===================================                                   |  50%  |                                                                              |==========================================                            |  60%  |                                                                              |=================================================                     |  70%  |                                                                              |========================================================              |  80%  |                                                                              |===============================================================       |  90%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |====                                                                  |   6%  |                                                                              |========                                                              |  11%  |                                                                              |============                                                          |  17%  |                                                                              |================                                                      |  22%  |                                                                              |===================                                                   |  28%  |                                                                              |=======================                                               |  33%  |                                                                              |===========================                                           |  39%  |                                                                              |===============================                                       |  44%  |                                                                              |===================================                                   |  50%  |                                                                              |=======================================                               |  56%  |                                                                              |===========================================                           |  61%  |                                                                              |===============================================                       |  67%  |                                                                              |===================================================                   |  72%  |                                                                              |======================================================                |  78%  |                                                                              |==========================================================            |  83%  |                                                                              |==============================================================        |  89%  |                                                                              |==================================================================    |  94%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |====                                                                  |   6%  |                                                                              |========                                                              |  12%  |                                                                              |============                                                          |  18%  |                                                                              |================                                                      |  24%  |                                                                              |=====================                                                 |  29%  |                                                                              |=========================                                             |  35%  |                                                                              |=============================                                         |  41%  |                                                                              |=================================                                     |  47%  |                                                                              |=====================================                                 |  53%  |                                                                              |=========================================                             |  59%  |                                                                              |=============================================                         |  65%  |                                                                              |=================================================                     |  71%  |                                                                              |======================================================                |  76%  |                                                                              |==========================================================            |  82%  |                                                                              |==============================================================        |  88%  |                                                                              |==================================================================    |  94%  |                                                                              |======================================================================| 100%
stopifnot(nrow(regional) > 0, ncol(identity) == ncol(real_data))
plotRegionalPathways(regional, database = database)

The ranks interface uses the complete measured feature ranking, with no FDR or foreground prefilter. It retains every used rank and the complete eligible pathway family. Competitive feature-set P values do not establish reproducibility across specimens. Joint gene/metabolite ranks can be RMS-scaled per modality, but pathways with many measured genes can still be dominated by transcriptomic coverage.

For metabolomics, createPathwayAssay(metabolite_ambiguity="exclude") excludes mass features with multiple candidate compound identities. Original candidates, exclusions, aggregation weights and measured members remain inspectable. The workflow uses this policy by default; the public native interface retains the historical expand option. Sparse matrices stay sparse during identity aggregation. The network viewer can consume this identity assay directly, preventing re-expansion of excluded mass candidates. GESECA, scores and networks therefore use the same identity definition as regional enrichment.

The pathway dot plot uses adjusted P values for significance outlines and 1 - Jaccard similarity for pathway ordering. It handles a single pathway. Mouse genes are not silently uppercased into a human HGNC index; the registered mouse recipes currently show compound pathways until an explicit compatible gene identity or orthology reference is supplied.

Run the real mouse-brain reference recipes

prepare <- system.file("workflows", "prepare_mouse_brain.R", package = "SpaMTP")
run <- system.file("workflows", "run_mouse_brain.R", package = "SpaMTP")
source(prepare)
source(run)
input_directory <- tempfile("mouse-brain-inputs-")

Preparation reads only the pinned native SpaMTPData 1.1.0 resources. With nativeDir = NULL, the public reader resolves verified local files, its cache or the published download. Supplying a directory selects offline mode by default; offline = TRUE can also require a previously populated cache. It does not load Seurat or deserialize historical Seurat objects. H&E is optional: the published native resources contain expression and coordinates but omit optical rasters. Independent image files can be attached with addSpatialImage(); their checksums, explicit scale factors and source provenance are retained.

For FMP10/Visium pixel-overlap mapping, supply a measured Visium spot radius in the original image coordinate system. A nearest-neighbour distance is not a spot diameter. Without a radius, preparation still produces usable DHB inputs, but the FMP10 runner stops with an explicit geometry requirement. The optional JSON configuration accepts source, target_radius and images. Image paths are resolved relative to the R process working directory, not the JSON file.

For this exact registered pair, the small bundled scale-factor JSON retains the values from the original Space Ranger outs/spatial/scalefactors_json.json for V11L12-109_B1, from Vicari et al.. Its original file MD5 is 412630d2d6be8c828bcf13116c64c315. All 3,120 Visium barcodes and both coordinates match the original tissue-position table exactly. The native convention is x = pxl_row_in_fullres and y = pxl_col_in_fullres; swapping these axes does not change the isotropic radius. No H&E raster is attached here. Do not reuse this radius for another sample or attach an image without checking axis orientation and scale.

scales <- jsonlite::read_json(system.file("extdata",
    "mouse_brain_scalefactors.json", package = "SpaMTP"))
config <- list(
    source = paste("V11L12-109_B1 Space Ranger scalefactors_json.json;",
                   "https://doi.org/10.17632/w7nw4km7xd.1"),
    target_radius = scales$spot_diameter_fullres / 2
)
config_file <- tempfile(fileext = ".json")
jsonlite::write_json(config, config_file, auto_unbox = TRUE,
                    pretty = TRUE, digits = NA)
pairing <- prepareMouseBrain(outputDir = input_directory,
                            spatialConfig = config_file)
stopifnot(pairing$target_radius == 188.471)
pairing[c("coordinate_units", "pixel_width", "target_radius")]
## $coordinate_units
## [1] "original image pixels"
## 
## $pixel_width
## [1] 577.536
## 
## $target_radius
## [1] 188.471

The following calls execute both full analyses and render their reports. Output directories are new temporary paths, so rebuilding never overwrites a previous analysis. To retain reports across R sessions, choose new permanent paths.

mouse_outputs <- setNames(lapply(c("dhb", "fmp10"), function(case) {
    output <- tempfile(paste0("mouse-brain-", case, "-"))
    result <- runMouseBrain(input_directory, outputDir = output, case = case)
    stopifnot(file.exists(result$report),
              file.exists(file.path(output, "analysis.rds")))
    list(directory = output, report = result$report,
         metrics = result$structure$metrics)
}), c("dhb", "fmp10"))
##   |                                                                              |                                                                      |   0%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |======================================================================| 100%
## Pathway network written to /tmp/RtmpMpoxsC/mouse-brain-dhb-26a743cce8ec/.spamtp-report-26a7322860a6/widgets/network_1/RegionLoupe_2026_09_25_08_17_45.html
##       representation  k approx_silhouette reference_ARI conditional_stability
## 1         main / PCA  4        0.12882606 -0.0019061865             0.4933855
## 2         main / PCA  6        0.11246543  0.0016213811             0.5552388
## 3         main / PCA  8        0.10666839  0.0007071816             0.4800462
## 4         main / PCA 10        0.10732307  0.0021782963             0.5491521
## 5   main / Graph PCA  4        0.14801815  0.0303762806             0.8123963
## 6   main / Graph PCA  6        0.16247278  0.0130904818             0.7442735
## 7   main / Graph PCA  8        0.15205488  0.0178266971             0.7655359
## 8   main / Graph PCA 10        0.14973794  0.0255500627             0.5781605
## 9          SPT / PCA  4        0.09408991  0.0932814885             0.7829793
## 10         SPT / PCA  6        0.07543536  0.0751102557             0.5239660
## 11         SPT / PCA  8        0.07588000  0.0398389534             0.4609613
## 12         SPT / PCA 10        0.06846807  0.0434329803             0.5365267
## 13   SPT / Graph PCA  4        0.14817514  0.1225235932             0.6248041
## 14   SPT / Graph PCA  6        0.14385890  0.0983346921             0.8760259
## 15   SPT / Graph PCA  8        0.17218604  0.0729354091             0.7063765
## 16   SPT / Graph PCA 10        0.17966847  0.0507772070             0.7685153
## 17       Joint / PCA  4        0.04417659  0.0718672069             0.6119008
## 18       Joint / PCA  6        0.04806496  0.0750478834             0.7445387
## 19       Joint / PCA  8        0.05012798  0.0435112477             0.5889424
## 20       Joint / PCA 10        0.04939911  0.0509717975             0.4905266
## 21 Joint / Graph PCA  4        0.07708606  0.0727363485             0.6058742
## 22 Joint / Graph PCA  6        0.09488760  0.0599800503             0.6494328
## 23 Joint / Graph PCA  8        0.11766077  0.0442641917             0.6856184
## 24 Joint / Graph PCA 10        0.13452642  0.0278014914             0.7211090
##    stability_omissions spatial_neighbor_agreement smallest_cluster
## 1                    6                  0.3950564              169
## 2                    6                  0.2970512               82
## 3                    6                  0.2606245               80
## 4                    6                  0.2649610               42
## 5                    6                  0.8499566               87
## 6                    6                  0.8104944                9
## 7                    6                  0.7424111                8
## 8                    6                  0.6912402                8
## 9                    6                  0.4930616               70
## 10                   6                  0.4067650               26
## 11                   6                  0.3581960               25
## 12                   6                  0.3265395               20
## 13                   6                  0.8707719               30
## 14                   6                  0.8456201               21
## 15                   6                  0.8677363               21
## 16                   6                  0.8703382               19
## 17                   6                  0.7714657              164
## 18                   6                  0.7567216                1
## 19                   6                  0.6270598                1
## 20                   6                  0.5494363                1
## 21                   6                  0.9518647              151
## 22                   6                  0.9332177               25
## 23                   6                  0.9189072               13
## 24                   6                  0.9080659               13
## Report: /tmp/RtmpMpoxsC/mouse-brain-dhb-26a743cce8ec/report/report.html
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 140950)
##       representation  k approx_silhouette reference_ARI conditional_stability
## 1          RNA / PCA  6        0.28578767    0.49733693             0.8663156
## 2          RNA / PCA  8        0.23459824    0.43099473             0.8512984
## 3          RNA / PCA 10        0.20799492    0.36000361             0.8392286
## 4          RNA / PCA 12        0.19864245    0.31379846             0.8927334
## 5    RNA / Graph PCA  6        0.32119478    0.52866773             0.8665574
## 6    RNA / Graph PCA  8        0.25842436    0.41961637             0.7952983
## 7    RNA / Graph PCA 10        0.25750551    0.37940085             0.8241918
## 8    RNA / Graph PCA 12        0.24895124    0.34236789             0.7054668
## 9           MS / PCA  6        0.15565324    0.07152823             0.6269252
## 10          MS / PCA  8        0.15628860    0.09798186             0.6465372
## 11          MS / PCA 10        0.13685486    0.07630411             0.5738940
## 12          MS / PCA 12        0.12277979    0.07147229             0.6566675
## 13    MS / Graph PCA  6        0.15088841    0.16740290             0.7156160
## 14    MS / Graph PCA  8        0.16556996    0.22082716             0.7851669
## 15    MS / Graph PCA 10        0.16791963    0.19647568             0.6697844
## 16    MS / Graph PCA 12        0.15061243    0.16514566             0.5513087
## 17       Joint / PCA  6        0.05494144    0.50487131             0.6885133
## 18       Joint / PCA  8        0.06434286    0.42968421             0.7725595
## 19       Joint / PCA 10        0.07456193    0.42941402             0.7390452
## 20       Joint / PCA 12        0.07811564    0.37972722             0.7175251
## 21 Joint / Graph PCA  6        0.09849265    0.47526192             0.7485592
## 22 Joint / Graph PCA  8        0.12329198    0.31959472             0.7278905
## 23 Joint / Graph PCA 10        0.13799716    0.33600840             0.7786624
## 24 Joint / Graph PCA 12        0.15883638    0.33327462             0.8200838
##    stability_omissions spatial_neighbor_agreement smallest_cluster
## 1                    6                  0.7190505               84
## 2                    6                  0.6152161               76
## 3                    6                  0.5650639               75
## 4                    6                  0.5263542               75
## 5                    6                  0.8738892              123
## 6                    6                  0.8314060               95
## 7                    6                  0.8031649               81
## 8                    6                  0.7895313               77
## 9                    6                  0.3942788               37
## 10                   6                  0.3808886                8
## 11                   6                  0.3454656                3
## 12                   6                  0.3026172                1
## 13                   6                  0.7303713              210
## 14                   6                  0.7123554               93
## 15                   6                  0.6759586               12
## 16                   6                  0.6510043               12
## 17                   6                  0.8305539               71
## 18                   6                  0.7810103               71
## 19                   6                  0.7822276                3
## 20                   6                  0.7349970                3
## 21                   6                  0.9102861              260
## 22                   6                  0.9097991              199
## 23                   6                  0.8937310              147
## 24                   6                  0.8797322               90
## Report: /tmp/RtmpMpoxsC/mouse-brain-fmp10-26a73ae039ad/report/report.html
mouse_outputs$dhb$metrics
##       representation  k approx_silhouette reference_ARI conditional_stability
## 1         main / PCA  4        0.12882606 -0.0019061865             0.4933855
## 2         main / PCA  6        0.11246543  0.0016213811             0.5552388
## 3         main / PCA  8        0.10666839  0.0007071816             0.4800462
## 4         main / PCA 10        0.10732307  0.0021782963             0.5491521
## 5   main / Graph PCA  4        0.14801815  0.0303762806             0.8123963
## 6   main / Graph PCA  6        0.16247278  0.0130904818             0.7442735
## 7   main / Graph PCA  8        0.15205488  0.0178266971             0.7655359
## 8   main / Graph PCA 10        0.14973794  0.0255500627             0.5781605
## 9          SPT / PCA  4        0.09408991  0.0932814885             0.7829793
## 10         SPT / PCA  6        0.07543536  0.0751102557             0.5239660
## 11         SPT / PCA  8        0.07588000  0.0398389534             0.4609613
## 12         SPT / PCA 10        0.06846807  0.0434329803             0.5365267
## 13   SPT / Graph PCA  4        0.14817514  0.1225235932             0.6248041
## 14   SPT / Graph PCA  6        0.14385890  0.0983346921             0.8760259
## 15   SPT / Graph PCA  8        0.17218604  0.0729354091             0.7063765
## 16   SPT / Graph PCA 10        0.17966847  0.0507772070             0.7685153
## 17       Joint / PCA  4        0.04417659  0.0718672069             0.6119008
## 18       Joint / PCA  6        0.04806496  0.0750478834             0.7445387
## 19       Joint / PCA  8        0.05012798  0.0435112477             0.5889424
## 20       Joint / PCA 10        0.04939911  0.0509717975             0.4905266
## 21 Joint / Graph PCA  4        0.07708606  0.0727363485             0.6058742
## 22 Joint / Graph PCA  6        0.09488760  0.0599800503             0.6494328
## 23 Joint / Graph PCA  8        0.11766077  0.0442641917             0.6856184
## 24 Joint / Graph PCA 10        0.13452642  0.0278014914             0.7211090
##    stability_omissions spatial_neighbor_agreement smallest_cluster
## 1                    6                  0.3950564              169
## 2                    6                  0.2970512               82
## 3                    6                  0.2606245               80
## 4                    6                  0.2649610               42
## 5                    6                  0.8499566               87
## 6                    6                  0.8104944                9
## 7                    6                  0.7424111                8
## 8                    6                  0.6912402                8
## 9                    6                  0.4930616               70
## 10                   6                  0.4067650               26
## 11                   6                  0.3581960               25
## 12                   6                  0.3265395               20
## 13                   6                  0.8707719               30
## 14                   6                  0.8456201               21
## 15                   6                  0.8677363               21
## 16                   6                  0.8703382               19
## 17                   6                  0.7714657              164
## 18                   6                  0.7567216                1
## 19                   6                  0.6270598                1
## 20                   6                  0.5494363                1
## 21                   6                  0.9518647              151
## 22                   6                  0.9332177               25
## 23                   6                  0.9189072               13
## 24                   6                  0.9080659               13
mouse_outputs$fmp10$metrics
##       representation  k approx_silhouette reference_ARI conditional_stability
## 1          RNA / PCA  6        0.28578767    0.49733693             0.8663156
## 2          RNA / PCA  8        0.23459824    0.43099473             0.8512984
## 3          RNA / PCA 10        0.20799492    0.36000361             0.8392286
## 4          RNA / PCA 12        0.19864245    0.31379846             0.8927334
## 5    RNA / Graph PCA  6        0.32119478    0.52866773             0.8665574
## 6    RNA / Graph PCA  8        0.25842436    0.41961637             0.7952983
## 7    RNA / Graph PCA 10        0.25750551    0.37940085             0.8241918
## 8    RNA / Graph PCA 12        0.24895124    0.34236789             0.7054668
## 9           MS / PCA  6        0.15565324    0.07152823             0.6269252
## 10          MS / PCA  8        0.15628860    0.09798186             0.6465372
## 11          MS / PCA 10        0.13685486    0.07630411             0.5738940
## 12          MS / PCA 12        0.12277979    0.07147229             0.6566675
## 13    MS / Graph PCA  6        0.15088841    0.16740290             0.7156160
## 14    MS / Graph PCA  8        0.16556996    0.22082716             0.7851669
## 15    MS / Graph PCA 10        0.16791963    0.19647568             0.6697844
## 16    MS / Graph PCA 12        0.15061243    0.16514566             0.5513087
## 17       Joint / PCA  6        0.05494144    0.50487131             0.6885133
## 18       Joint / PCA  8        0.06434286    0.42968421             0.7725595
## 19       Joint / PCA 10        0.07456193    0.42941402             0.7390452
## 20       Joint / PCA 12        0.07811564    0.37972722             0.7175251
## 21 Joint / Graph PCA  6        0.09849265    0.47526192             0.7485592
## 22 Joint / Graph PCA  8        0.12329198    0.31959472             0.7278905
## 23 Joint / Graph PCA 10        0.13799716    0.33600840             0.7786624
## 24 Joint / Graph PCA 12        0.15883638    0.33327462             0.8200838
##    stability_omissions spatial_neighbor_agreement smallest_cluster
## 1                    6                  0.7190505               84
## 2                    6                  0.6152161               76
## 3                    6                  0.5650639               75
## 4                    6                  0.5263542               75
## 5                    6                  0.8738892              123
## 6                    6                  0.8314060               95
## 7                    6                  0.8031649               81
## 8                    6                  0.7895313               77
## 9                    6                  0.3942788               37
## 10                   6                  0.3808886                8
## 11                   6                  0.3454656                3
## 12                   6                  0.3026172                1
## 13                   6                  0.7303713              210
## 14                   6                  0.7123554               93
## 15                   6                  0.6759586               12
## 16                   6                  0.6510043               12
## 17                   6                  0.8305539               71
## 18                   6                  0.7810103               71
## 19                   6                  0.7822276                3
## 20                   6                  0.7349970                3
## 21                   6                  0.9102861              260
## 22                   6                  0.9097991              199
## 23                   6                  0.8937310              147
## 24                   6                  0.8797322               90

These panels are taken from the reports just generated, not from saved reference figures. They compare spatial structure for both full real datasets. Spatial smoothing alone is not evidence of improved biological recovery.

spatial_figures <- vapply(mouse_outputs, function(output) {
    directory <- dirname(output$report)
    figures <- read.csv(file.path(directory, "figure_index.csv"))
    selected <- figures$png[figures$name == "structure_spatial"]
    stopifnot(length(selected) == 1L)
    file.path(directory, selected)
}, character(1))
knitr::include_graphics(unname(spatial_figures))
DHB / Visium: all retained observations.

DHB / Visium: all retained observations.

FMP10 / Visium: all retained, overlap-mapped observations.

FMP10 / Visium: all retained, overlap-mapped observations.

Pairing is documented for these registered source specimens; arbitrary datasets are not paired merely because both are mouse brains. DHB already contains paired modalities. Both runners re-annotate with the pinned chemical database and an explicit positive-mode DHB or FMP-10 index, rather than using the archived mass-only annotation cache. Ambiguous compound assignments remain excluded; mouse RNA is retained without human-HGNC or implicit orthology conversion.

The installed network-only demo is another native entry point:

demo <- system.file("examples", "pathway_network_mouse_brain_demo.R",
                    package = "SpaMTP")
source(demo)
network_directory <- tempfile("mouse-brain-networks-")
networks <- runMouseBrainNetworkDemo(outputDir = network_directory,
                                    ident = "RegionLoupe")
## Building sorted adduct candidate index ...
## Searching candidate index at 5 ppm ...
## Adding annotations to rowData ....
##   |                                                                              |                                                                      |   0%  |                                                                              |======================================================================| 100%
## 
##   |                                                                              |                                                                      |   0%  |                                                                              |==================                                                    |  25%  |                                                                              |===================================                                   |  50%  |                                                                              |====================================================                  |  75%  |                                                                              |======================================================================| 100%
## Pathway network written to /tmp/RtmpMpoxsC/mouse-brain-networks-26a76036f777/leading_edge/RegionLoupe_2026_09_25_08_34_25.html
## Pathway network written to /tmp/RtmpMpoxsC/mouse-brain-networks-26a76036f777/annotated/RegionLoupe_2026_09_25_08_34_36.html
stopifnot(file.exists(file.path(network_directory, "network_analysis.rds")))
networks$status
## [1] "completed"
length(unlist(networks$networks))
## [1] 2

It uses findAllDEMs(method="markers") for descriptive regional effects, complete compound rankings for competitive pathway enrichment, and pathwayNetworkPlots() for separate leading-edge and annotation-coverage views. It does not assign biological-replicate significance to individual pixels or map mouse gene symbols to human genes. Outputs include the native analysis RDS, annotation/mapping audits and, when pathways are eligible, offline HTML networks. No eligible pathways is reported explicitly rather than relaxing the filters.

Render and retain a reviewable analysis

report <- renderSpaMTPReport(fit, tempfile("teaching-report-"))
stopifnot(file.exists(report))

Reports follow the analytical sequence above. They include a linked region/DE explorer with feature search, directional AUC/effect/stability/FDR filters, spatial zoom and pan, coordinated feature profiles and complete CSV exports. Native 3D and network views are available when their input requirements are satisfied. Figures are embedded as PNGs and also exported as PDFs. Open the HTML in a browser or an IDE HTML preview that permits JavaScript; Markdown previews may disable interaction. The output directory also contains the complete workflow RDS, settings, inputs used by each stage, source and database provenance, and file checksums.

For methods, see the Bioconductor marker-scoring documentation and the GraphPCA paper.

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] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] SpaMTP_0.99.10   BiocStyle_2.41.0
## 
## loaded via a namespace (and not attached):
##   [1] splines_4.6.1               later_1.4.8                
##   [3] filelock_1.0.3              tibble_3.3.1               
##   [5] lifecycle_1.0.5             httr2_1.3.0                
##   [7] sf_1.1-3                    edgeR_4.99.6               
##   [9] globals_0.19.1              lattice_0.23-1             
##  [11] crosstalk_1.2.2             magrittr_2.0.5             
##  [13] SpaMTPdb_0.99.4             limma_3.99.0               
##  [15] plotly_4.12.1               sass_0.4.10                
##  [17] rmarkdown_2.32              jquerylib_0.1.4            
##  [19] yaml_2.3.12                 metapod_1.21.0             
##  [21] httpuv_1.6.17               otel_0.2.0                 
##  [23] cowplot_1.2.0               DBI_1.3.0                  
##  [25] buildtools_1.0.0            RColorBrewer_1.1-3         
##  [27] abind_1.4-8                 GenomicRanges_1.65.4       
##  [29] purrr_1.2.2                 BiocGenerics_0.59.12       
##  [31] rappdirs_0.3.4              SpaMTPData_0.99.5          
##  [33] IRanges_2.47.5              S4Vectors_0.51.10          
##  [35] ggrepel_0.9.8               irlba_2.3.7                
##  [37] listenv_1.0.0               maketools_1.3.2            
##  [39] pheatmap_1.0.13             units_1.0-1                
##  [41] RSpectra_0.16-2             matter_2.15.0              
##  [43] dqrng_0.4.1                 parallelly_1.48.0          
##  [45] codetools_0.2-20            DelayedArray_0.39.7        
##  [47] scuttle_1.23.2              tidyselect_1.2.1           
##  [49] farver_2.1.2                ScaledMatrix_1.21.0        
##  [51] viridis_0.6.5               matrixStats_1.5.0          
##  [53] stats4_4.6.1                BiocFileCache_3.3.0        
##  [55] Seqinfo_1.3.2               jsonlite_2.0.0             
##  [57] CardinalIO_1.11.0           BiocNeighbors_2.7.3        
##  [59] e1071_1.7-17                scater_1.41.2              
##  [61] tools_4.6.1                 Rcpp_1.1.2                 
##  [63] glue_1.8.1                  gridExtra_2.3.1            
##  [65] SparseArray_1.13.3          BiocBaseUtils_1.15.1       
##  [67] xfun_0.61                   MatrixGenerics_1.25.0      
##  [69] dplyr_1.2.1                 withr_3.0.3                
##  [71] BiocManager_1.30.27         fastmap_1.2.0              
##  [73] bluster_1.23.1              digest_0.6.39              
##  [75] rsvd_1.0.5                  R6_2.6.1                   
##  [77] mime_0.13                   RSQLite_3.53.3             
##  [79] tidyr_1.3.2                 generics_0.1.4             
##  [81] data.table_1.18.6.1         FNN_1.1.4.1                
##  [83] class_7.3-24                httr_1.4.9                 
##  [85] htmlwidgets_1.6.4           S4Arrays_1.13.1            
##  [87] ontologyIndex_2.12          uwot_0.2.5                 
##  [89] pkgconfig_2.0.3             gtable_0.3.6               
##  [91] blob_1.3.0                  S7_0.2.2                   
##  [93] SingleCellExperiment_1.35.2 XVector_0.53.0             
##  [95] sys_3.4.3                   htmltools_0.5.9            
##  [97] fgsea_1.39.2                ProtGenerics_1.45.0        
##  [99] scales_1.4.0                Biobase_2.73.2             
## [101] png_0.1-9                   SpatialExperiment_1.23.0   
## [103] scran_1.41.1                knitr_1.52                 
## [105] rjson_0.2.23                nlme_3.1-171               
## [107] curl_8.0.0                  proxy_0.4-29               
## [109] cachem_1.1.0                stringr_1.6.0              
## [111] Cardinal_3.15.0             BiocVersion_3.24.0         
## [113] KernSmooth_2.23-27          parallel_4.6.1             
## [115] vipor_0.4.7                 AnnotationDbi_1.75.2       
## [117] pillar_1.11.1               grid_4.6.1                 
## [119] vctrs_0.7.3                 promises_1.5.0             
## [121] BiocSingular_1.29.1         dbplyr_2.6.0               
## [123] beachmat_2.29.3             xtable_1.8-8               
## [125] cluster_2.1.8.3             beeswarm_0.4.0             
## [127] evaluate_1.0.5              magick_2.9.1               
## [129] cli_3.6.6                   locfit_1.5-9.12            
## [131] compiler_4.6.1              rlang_1.3.0                
## [133] crayon_1.5.3                future.apply_1.20.2        
## [135] labeling_0.4.3              mclust_6.1.3               
## [137] classInt_0.4-11             naturalsort_0.1.3          
## [139] ggbeeswarm_0.7.3            stringi_1.8.9              
## [141] viridisLite_0.4.3           BiocParallel_1.47.0        
## [143] Biostrings_2.81.9           Matrix_1.7-6               
## [145] ExperimentHub_3.3.2         bit64_4.8.6                
## [147] future_1.76.0               ggplot2_4.0.3              
## [149] KEGGREST_1.53.6             statmod_1.5.2              
## [151] shiny_1.14.0                SummarizedExperiment_1.43.0
## [153] AnnotationHub_4.3.2         igraph_2.3.3               
## [155] memoise_2.0.1               bslib_0.12.0               
## [157] fastmatch_1.1-8             bit_4.6.0