SBIVAR: Spatial BIVARiate association tests across disjoint coordinate sets

This repo provides code for performing bivariate association tests between different spatial modalities measured on the same or consecutive slices, possibly with disjoint coordinate sets. A common coordinate framework as obtained from alignment is considered given. An extensive description of the methods is available in our preprint.

The package can be installed from GitHub (with suggested packages through dependencies = TRUE) as follows:

remotes::install_github("sthawinke/sbivar", build_vignettes = TRUE, dependencies = TRUE)

Once installed, you can load the package

library(sbivar)

As example dataset, we use the one by Vicari et al. (2024), which includes replicated measurements of spatial transcriptomics and metabolomics coprofiled on mouse brain sections. A subset of the data is available in the sbivar package and can be loaded as:

data(Vicari)

This object consists of four lists of length six: MetaboliteCoords and TranscriptCoords for the coordinates, and TranscriptOutcomes and MetaboliteOutcomes for the feature measurements. Have a look at the aligned coordinates:

par(mfrow = c(2, 3))
plotCoordsMulti(Vicari$TranscriptCoords, Vicari$MetaboliteCoords, cex = 0.1)

par(mfrow = c(1, 1))

Single-image analysis

The Vicari data consist of six images which could be analysed jointly, but for didactical purposes we also analyse a single image here, the sample “V11L12-109_A1”.

singleSample <- "V11L12-109_A1"
singleStxCoords <- Vicari$TranscriptCoords[[singleSample]]
singleStx <- Vicari$TranscriptOutcomes[[singleSample]]
singleMetCoords <- Vicari$MetaboliteCoords[[singleSample]]
singleMet <- Vicari$MetaboliteOutcomes[[singleSample]]

Prepare multithreading

library(BiocParallel)
nCores <- 2
if (.Platform$OS.type == "unix") {
    # On unix-based systems (linux and macOS), use MulticoreParam
    register(MulticoreParam(nCores, stop.on.error = FALSE))
} else {
    # On windows, use SnowParam
    register(SnowParam(workers = nCores, type = "SOCK", stop.on.error = FALSE))
}

Now analyse this single sample using bivariate Moran’s I.

moranRes <- sbivar(singleStx, singleMet, singleStxCoords, singleMetCoords,
    method = "Moran's I"
)
## Starting sbivar analysis of a single image on 2 computing cores

## Testing significance of bivariate Moran's I for 25 feature pairs

## Calculating bivariate Moran's I statistics ...

## Fitting variograms for first modality (5 features) ...

## Fitting variograms for second modality (5 features) ...

## Calculating variances of bivariate Moran's I statistics ...

Have a look at the results:

topPairs(moranRes)
##   Modality_X Modality_Y     Ixy_5e.06     Ixy_2e.04      Ixy_0.02 SE.Ixy._5e.06
## 1       Pcp4   Dopamine  1.691068e-04  1.313399e-04  6.215548e-05  4.471070e-05
## 2    mt.Atp6  Histidine  5.174945e-05  2.628488e-05  6.816206e-06  1.825502e-05
## 3       Fth1  Histidine -1.456025e-05 -2.624468e-05 -1.000125e-05  1.823251e-05
## 4       Pcp4  Histidine  2.981628e-05  1.236840e-05  3.038781e-06  1.823146e-05
## 5       Fth1 Tocopherol -1.413589e-05 -1.422649e-05 -3.450981e-06  1.812031e-05
## 6    mt.Atp6    Taurine  1.486419e-06  1.480923e-05  4.817983e-06  1.812031e-05
##   SE.Ixy._2e.04 SE.Ixy._0.02         pVal       pAdj
## 1  4.147731e-05 2.876564e-05 0.0004216507 0.01054127
## 2  7.455806e-06 3.728807e-06 0.0011548134 0.01443517
## 3  7.830945e-06 3.390757e-06 0.0019246076 0.01603840
## 4  6.869515e-06 3.390383e-06 0.1167989515 0.43101376
## 5  7.565471e-06 2.874446e-06 0.1361841917 0.43101376
## 6  7.116155e-06 3.146162e-06 0.1462119192 0.43101376

Plot the most significantly spatially associated gene-metabolite pair Pcp4–Dopamine:

plotTopPair(moranRes, singleStx, singleMet, singleStxCoords, singleMetCoords, size = 1)

Write the results to a spreadsheet

writeSbivarToXlsx(moranRes, file = "myfile.xlsx")

Multi-image analysis

Next, we analyse the six images jointly. First construct a variable identifying the mouse, consisting of the first 10 characters of the names:

mouse <- substr(names(Vicari$TranscriptOutcomes), 1, 10)

For the multi-image case, we use GAMs as measure of spatial association, with a negative binomial outcome distribution for the transcriptome data, and a gamma outcome distribution for the metabolome data. A log-link is used in both cases.

multiGAMRes <- sbivar(
    X = Vicari$TranscriptOutcomes, Y = Vicari$MetaboliteOutcomes,
    Cx = Vicari$TranscriptCoords, Ey = Vicari$MetaboliteCoords,
    method = "GAM", families = list("X" = mgcv::nb(), "Y" = Gamma(lin = "log"))
)
## Starting sbivar analysis (GAMs) of 6 images on 2 computing cores

## Image 1 of 6

## Image 2 of 6

## Image 3 of 6

## Image 4 of 6

## Image 5 of 6

## Image 6 of 6

Next we plug the calculated correlations between the spline surfaces into a linear model, with random effects for the individual mice:

design <- data.frame("mouse" = mouse)
multiGAMLmms <- fitLinModels(multiGAMRes, design, Formula = ~ (1 | mouse))
## Fitting 25 mixed effects models on 2 cores

Extract the results for the desired parameter (the intercept)

multiGAMLmmsRes <- extractResultsMulti(multiGAMLmms, design)
topPairs(multiGAMLmmsRes, parameter = "Intercept")
##   Modality_X Modality_Y   Estimate         SE       pVal      pAdj
## 1      Sparc Tocopherol -0.1969455 0.05512814 0.01600122 0.4000305
## 2       Gnas Tocopherol  0.2058657 0.08376740 0.05739854 0.5648194
## 3       Gnas       GABA  0.2549455 0.10973641 0.06777833 0.5648194
## 4       Gnas  Histidine  0.2027837 0.11076368 0.12664093 0.7915058
## 5       Fth1       GABA  0.4125944 0.19797769 0.28481526 0.9377581
## 6       Gnas   Dopamine -0.2579455 0.15311234 0.34103084 0.9377581

No features are significantly associated after multiplicity correction. For illustration, we plot the feature pair with the smallest p-values nevertheless:

plotTopPair(multiGAMLmmsRes,
    Xl = Vicari$TranscriptOutcomes, Yl = Vicari$MetaboliteOutcomes,
    Cxl = Vicari$TranscriptCoords, Eyl = Vicari$MetaboliteCoords, size = 0.3
)

A more extensive description of the sbivar functionality can be found in the vignette, accessed by calling

browseVignettes("sbivar")