| Title: | Proximity-Associated Changes in Expression for Spatial Transcriptomics |
|---|---|
| Description: | PACE (Proximity-Associated Changes in Expression) is a hierarchical empirical Bayes framework that quantifies how a cell's gene expression changes with proximity to specific neighbouring cell types in imaging-based spatial transcriptomics such as Xenium and CosMx. It fits negative binomial mixed models with partial pooling across cell types, corrects for transcript contamination between adjacent cells with a per-cell ambient term, and decomposes expression variance into cell-type identity, spatial cell state, contamination, and residual components. Input and output follow SpatialExperiment conventions. |
| Authors: | Elijah Willie [aut, cre] (ORCID: <https://orcid.org/0000-0002-5773-3649>), Shreya Rajesh Rao [aut], John Ormerod [aut], Ellis Patrick [aut], Australian Government Research Training Program [fnd] |
| Maintainer: | Elijah Willie <[email protected]> |
| License: | GPL-3 |
| Version: | 0.99.18 |
| Built: | 2026-10-14 02:11:04 UTC |
| Source: | https://github.com/BiocStaging/PACE |
Returns the sparse cross-cell-type ambient weight matrix that defines the per-cell contamination term (the short-range technical field a cell receives from its heterotypic neighbours).
ambientField(object, ...) ## S4 method for signature 'SpatialExperiment' ambientField( object, celltype_col, image_col = NULL, h_tech = 5, assay_name = "counts", ... )ambientField(object, ...) ## S4 method for signature 'SpatialExperiment' ambientField( object, celltype_col, image_col = NULL, h_tech = 5, assay_name = "counts", ... )
object |
|
... |
Passed to the ambient-field builder. |
celltype_col |
colData column with the cell-type annotation. |
image_col |
Optional colData column grouping cells into images. |
h_tech |
Technical bandwidth in micrometres (default 5). |
assay_name |
Counts assay name (default |
The ambient-field object (sparse weight matrix and image index).
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) af <- ambientField(spe, celltype_col = "cellType") names(af)spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) af <- ambientField(spe, celltype_col = "cellType") names(af)
Returns, for each cell type, the genes used as negative-control anchors when identifying the per-cell contamination loading: genes owned by another cell type (high mean expression there) that are near-absent in the focal type. Anchors are recomputed with the same selection as the fit, and ranked by expression in their owning cell type. This is the source of the manuscript's supplementary anchor-gene tables.
anchorGenes(object, spe, top_n = 10L, assay_name = "counts")anchorGenes(object, spe, top_n = 10L, assay_name = "counts")
object |
A PACEFit (supplies the cell-type and image columns used at fit time). |
spe |
The SpatialExperiment::SpatialExperiment that was fitted. |
top_n |
Number of top anchors to list per cell type (default 10). |
assay_name |
Counts assay name (default |
A data frame with one row per cell type: cellType, n_anchors
(total anchors), and top_anchors (the top_n strongest, comma-separated).
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) anchorGenes(fit, spe)spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) anchorGenes(fit, spe)
Returns the per-cell kernel-weighted neighbour abundances: the Gaussian
biological kernel K_bio (used for the proximity coefficients) and the
short-range exponential technical kernel K_tech. Useful for inspecting the
neighbourhood before or independently of a fit.
buildNeighbourhood(object, ...) ## S4 method for signature 'SpatialExperiment' buildNeighbourhood( object, celltype_col, h_bio = 30, h_tech = 5, eps = NULL, ... )buildNeighbourhood(object, ...) ## S4 method for signature 'SpatialExperiment' buildNeighbourhood( object, celltype_col, h_bio = 30, h_tech = 5, eps = NULL, ... )
object |
|
... |
Passed to the kernel builder. |
celltype_col |
colData column with the cell-type annotation. |
h_bio, h_tech
|
Bandwidths in micrometres (defaults 30, 5). |
eps |
Truncation radius; defaults to |
A list with K_bio and K_tech (cells x cell types).
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) nb <- buildNeighbourhood(spe, celltype_col = "cellType") dim(nb$K_bio)spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) nb <- buildNeighbourhood(spe, celltype_col = "cellType") dim(nb$K_bio)
Returns the per-cell contamination loading rho_i and the fraction of a
cell's expected counts attributed to its local ambient field. Under the
per-cell contamination model the expected count is
mu_ig = mu_bio_ig + rho_i * a_ig, where a_ig is the cross-cell-type
ambient field at cell i and rho_i is a single empirical-Bayes shrunken
loading shared across genes. The contamination fraction summarises that
second term over genes,
contamFraction_i = sum_g mu_spill_ig / sum_g mu_ig, and is the quantity
reported as contam_frac in the solver's fitting trace.
cellContamination(object, ...) ## S4 method for signature 'PACEFit' cellContamination(object, ...)cellContamination(object, ...) ## S4 method for signature 'PACEFit' cellContamination(object, ...)
object |
A PACEFit fitted with |
... |
Unused. |
A high contamination fraction marks a cell whose profile is substantially
explained by its neighbours rather than by its own cell type, which is the
expected signature of a segmentation or transcript-assignment error. It is
not a complete cell-quality score: because rho_i carries a fixed
gene-direction (the local ambient), it will under-report contamination whose
direction departs from the neighbourhood average, and a cell that is wholly a
segmentation artefact may fit some incorrect cell type with a low rho_i.
Pair it with ordinary per-cell quality control.
Read contamFraction rather than rho on its own. The ambient field is
cross-cell-type, so a cell with no differently-typed neighbour inside the
technical kernel has a_ig = 0 for every gene and therefore no contamination
at all. Its rho_i is then unidentified and shrinks to the empirical Bayes
prior mean, giving every such cell the same apparently middling loading. On
the shipped example fit this is 4,589 of 7,898 cells, 84% of the tumour cells
in a tumour-dominated crop. contamFraction reports them as zero, which is
correct; rho alone does not.
A data frame with one row per cell, in fitting order: cell (the
working-frame cell identifier, or the row index if the frame is unnamed),
celltype (the label the cell was fitted under), rho (the contamination
loading) and contamFraction.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) cc <- cellContamination(fit) head(cc) # Contamination is highest where cells sit against a different type. tapply(cc$contamFraction, cc$celltype, median)fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) cc <- cellContamination(fit) head(cc) # Contamination is highest where cells sit against a different type. tapply(cc$contamFraction, cc$celltype, median)
The cell types the fit was built on, in fitting order. The order matters: it indexes the rows of the random-effect design, so the readouts and the solver state agree on what a neighbour column refers to.
cellTypes(object, ...) ## S4 method for signature 'PACEFit' cellTypes(object, ...)cellTypes(object, ...) ## S4 method for signature 'PACEFit' cellTypes(object, ...)
object |
A PACEFit. |
... |
Unused. |
A character vector of cell type names.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) cellTypes(fit)fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) cellTypes(fit)
A fit stores the fitted means and the contamination offsets as dense cells by
genes matrices. On a real cohort those dominate its size, and nothing
downstream needs them kept: paceDecompose() and paceDrivers() read
per-cell-type summaries the fit also holds, and rebuild the matrices from the
counts when they are absent.
compactFit( object, which = c("mu", "mu_bio", "mu_spill", "technical_offset_mat", "bleed_offset_mat") )compactFit( object, which = c("mu", "mu_bio", "mu_spill", "technical_offset_mat", "bleed_offset_mat") )
object |
A PACEFit. |
which |
The matrices to drop. The default is all of them. |
Use this before saving a fit. Every readout still works afterwards, and re-decomposing a compacted fit reproduces the tables it already carries.
object, without the named matrices.
stashPACE() for keeping the readouts with the data.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) small <- compactFit(fit) c(before = object.size(fit), after = object.size(small)) |> format(units = "MB")fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) small <- compactFit(fit) c(before = object.size(fit), after = object.size(small)) |> format(units = "MB")
The penalised quasi-likelihood solver's output: fixed and random effects and their standard errors, gene-wise overdispersions, variance components, the per-cell contamination loadings, and the convergence history.
fitState(object, ...) ## S4 method for signature 'PACEFit' fitState(object, ...)fitState(object, ...) ## S4 method for signature 'PACEFit' fitState(object, ...)
object |
A PACEFit. |
... |
Unused. |
This is the fit itself rather than a readout of it, and it is exposed for
inspection and diagnostics. Prefer neighbourSlopes(),
varianceDecomposition() and topDrivers() for interpretation: those apply
the shrinkage and attribution the method is defined by, and the raw BLUPs
here do not.
A named list of solver state.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) dim(fitState(fit)$U) fitState(fit)$convergedfit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) dim(fitState(fit)$U) fitState(fit)$converged
Returns the shrunken per-(gene, focal, neighbour) proximity coefficients with their local false sign rate (lfsr). For condition cohorts the responder interaction terms are included.
neighbourSlopes(object, ...) neighbourSlopes(object) <- value ## S4 method for signature 'PACEFit' neighbourSlopes(object, ...) ## S4 replacement method for signature 'PACEFit' neighbourSlopes(object) <- valueneighbourSlopes(object, ...) neighbourSlopes(object) <- value ## S4 method for signature 'PACEFit' neighbourSlopes(object, ...) ## S4 replacement method for signature 'PACEFit' neighbourSlopes(object) <- value
object |
A PACEFit. |
... |
Unused. |
value |
A data frame of shrunken proximity coefficients. |
A data frame of shrunken slopes.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) head(neighbourSlopes(fit))fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) head(neighbourSlopes(fit))
Partitions per-gene expression variance into cell-type identity, spatial cell
state, contamination, and residual (plus a responder block for condition
cohorts), populating varianceDecomposition(). Needs the fitted object plus
the same SpatialExperiment used for paceModel() (to read the counts).
paceDecompose(object, ...) ## S4 method for signature 'PACEFit' paceDecompose(object, spe, ...)paceDecompose(object, ...) ## S4 method for signature 'PACEFit' paceDecompose(object, spe, ...)
object |
A PACEFit from |
... |
Unused. |
spe |
The SpatialExperiment::SpatialExperiment that was fitted. |
The decomposition needs per-cell-type statistics of the fitted means, which
the fit stores; it never builds a cells x genes matrix. An older fit without
those statistics has them computed from its stored fitted means, or, if those
were dropped, rebuilt exactly from the fit and spe a block of genes at a time.
The PACEFit with the variance decomposition added.
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit <- paceDecompose(fit, spe) head(varianceDecomposition(fit))spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit <- paceDecompose(fit, spe) head(varianceDecomposition(fit))
Ranks the genes mediating each focal-neighbour relationship by driver score,
populating topDrivers(). Requires paceShrink() and paceDecompose() to
have run first.
paceDrivers(object, ...) ## S4 method for signature 'PACEFit' paceDrivers(object, spe = NULL, pairs = NULL, ...)paceDrivers(object, ...) ## S4 method for signature 'PACEFit' paceDrivers(object, spe = NULL, pairs = NULL, ...)
object |
A PACEFit with shrunken slopes and a decomposition. |
... |
Unused. |
spe |
The SpatialExperiment::SpatialExperiment that was fitted. Needed only for an older fit that stores neither its fitted means nor their per-cell-type statistics. |
pairs |
Optional list of focal-neighbour pairs to score; |
The driver scores read each cell type's mean fitted mean, which the fit
stores. For an older fit saved without its fitted means or those statistics
they are rebuilt exactly from the fit and spe, as in paceDecompose(), a
block of genes at a time; pass spe for such a fit.
The PACEFit with the driver tables added.
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit <- paceDrivers(fit, spe) names(topDrivers(fit))spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit <- paceDrivers(fit, spe) names(topDrivers(fit))
paceFit() runs the full pipeline in one call:
paceModel() -> paceShrink() -> paceDecompose() -> paceDrivers(). For
step-by-step control (inspecting the fitted model, re-running the downstream
without refitting), call those stages directly.
paceFit(object, ...) ## S4 method for signature 'SpatialExperiment' paceFit(object, ..., pairs = NULL)paceFit(object, ...) ## S4 method for signature 'SpatialExperiment' paceFit(object, ..., pairs = NULL)
object |
|
... |
Arguments passed to |
pairs |
Optional list of focal-neighbour pairs for the driver tables. |
A fully populated PACEFit.
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- paceFit(spe, celltype_col = "cellType", verbose = FALSE) head(neighbourSlopes(fit))spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- paceFit(spe, celltype_col = "cellType", verbose = FALSE) head(neighbourSlopes(fit))
An S4 container for the result of a PACE fit. It holds the raw penalised
quasi-likelihood solver state together with the three interpretable output
layers: shrunken per-(gene, focal, neighbour) proximity slopes, the per-gene
variance decomposition, and the per-pair driver tables. Construct one with
paceFit(); read it out with neighbourSlopes(), varianceDecomposition(),
and topDrivers().
## S4 method for signature 'PACEFit' show(object)## S4 method for signature 'PACEFit' show(object)
object |
A PACEFit object. |
The PACEFit class itself is a container and is not returned by a
function documented here; construct one with paceFit(). The show method
is called for its side effect of printing a one-screen summary of the fit –
cell types, kernel bandwidths, contamination and dispersion settings, the
condition term if there is one, and which pipeline stages have run – and
returns NULL invisibly.
fitThe raw streaming PQL solver state (fixed and random effects, per-cell contamination loadings, gene-wise overdispersions, variance components).
neighbourSlopesA data frame of shrunken proximity coefficients, one row per gene, focal cell type and neighbour cell type, plus the condition term for condition cohorts, with the raw estimate, shrunken estimate, and local false sign rate (lfsr).
varianceDecompositionA list of per-gene, per-focal variance decomposition tables (cell-type identity, spatial cell state, contamination, residual; and, for condition cohorts, a responder spatial block).
topDriversA list of per-pair driver-score tables ranking the genes that mediate each focal-neighbour spatial relationship.
cellTypesThe cell types modelled, in fitting order.
paramsThe fitting parameters actually used (bandwidths, contamination and dispersion settings, condition column, and so on).
contextThe working frame and fixed-effect design retained from the fit
so that the downstream stages (paceShrink(), paceDecompose(),
paceDrivers()) can run without refitting.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit neighbourSlopes(fit)fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit neighbourSlopes(fit)
paceModel() builds the biological and technical neighbourhood kernels from
the spatial coordinates and cell-type labels and fits the hierarchical
negative binomial mixed model by streaming penalised quasi-likelihood (with
the per-cell contamination correction). It returns a PACEFit carrying only
the fitted model; the reporting layers are added by paceShrink(),
paceDecompose(), and paceDrivers() (or all at once by paceFit()).
paceModel(object, ...) ## S4 method for signature 'SpatialExperiment' paceModel( object, celltype_col, image_col = NULL, condition_col = NULL, assay_name = "counts", h_bio = 30, h_tech = 5, contamination = c("percell_hc", "none"), dispersion = c("nb1", "nb2"), kernel_per_image = FALSE, image_re = c("none", "intercept", "slopes", "condition_slopes"), types = NULL, resp_term = NULL, verbose = TRUE, ... )paceModel(object, ...) ## S4 method for signature 'SpatialExperiment' paceModel( object, celltype_col, image_col = NULL, condition_col = NULL, assay_name = "counts", h_bio = 30, h_tech = 5, contamination = c("percell_hc", "none"), dispersion = c("nb1", "nb2"), kernel_per_image = FALSE, image_re = c("none", "intercept", "slopes", "condition_slopes"), types = NULL, resp_term = NULL, verbose = TRUE, ... )
object |
A SpatialExperiment::SpatialExperiment with a counts assay and two-dimensional spatial coordinates. |
... |
Further arguments passed to the streaming fitter ( |
celltype_col |
colData column with the discrete cell-type annotation. |
image_col |
Optional colData column grouping cells into images/samples.
|
condition_col |
Optional colData column with a binary condition; enables the responder spatial block for multi-sample cohorts. |
assay_name |
Counts assay name (default |
h_bio, h_tech
|
Biological and technical kernel bandwidths in micrometres (defaults 30 and 5). |
contamination |
|
dispersion |
|
kernel_per_image |
If |
image_re |
Second image-level random-effect block: |
types |
Cell types in the order they should index the random-effect
blocks. |
resp_term |
Responder interaction term prefix for condition cohorts; if
|
verbose |
Whether to print progress. |
A PACEFit with the fitted model (reporting layers empty).
Three pass-through arguments change how much memory a fit needs, or trade exactness for time. Two of them are approximations that are ON by default.
ambient_mode"cache" (default) or "stream". The contamination
model needs an ambient field per cell and gene; "cache" materialises
that n by G product, "stream" recomputes each chunk's columns from the
weights and the counts. The numbers are identical. On a 1.2M cell,
5,001 gene panel the cached product alone is over 5 GB, so "stream" is
what makes a full transcriptome panel fit in memory at all.
alpha_warmupDefault 6. The per-gene dispersion MLE is re-fitted
only on the first alpha_warmup iterations and on the last one; in
between, alpha is frozen at its warmed-up value. Alpha typically settles
within five iterations while the MLE is a large share of each iteration's
cost, so this is on by default. It is an approximation: pass Inf to
re-fit the dispersion on every iteration. Lowering it below the default
does move results – on the breast cancer cohort 4 changes the number
of calls at lfsr < 0.05.
alpha_max_nDefault Inf, meaning the dispersion MLE sees every
cell. A finite value caps it at an even, deterministic subsample; the
estimator, the Brent search and its tolerance are unchanged. The
dispersion is one scalar per gene and its standard error falls as
1/sqrt(n), so most cells add little. Validate any cap on your own
cohort before trusting it. A cap of 50,000 reproduced two cohorts of
roughly 10^5 cells exactly, and on a 1.2M cell panel – where the same
cap is a far smaller fraction of the data – it left the number of calls
unchanged while swapping the identity of 72 of them. The binding
quantity is not the absolute subsample.
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- paceModel(spe, celltype_col = "cellType", verbose = FALSE) fitspe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- paceModel(spe, celltype_col = "cellType", verbose = FALSE) fit
The Gaussian, identity-link form of paceModel(), for continuous measurements
such as imaging mass cytometry protein intensities rather than counts.
paceModelGaussian(object, ...) ## S4 method for signature 'SpatialExperiment' paceModelGaussian( object, celltype_col, image_col = NULL, condition_col = NULL, assay_name = "intensity", h_bio = 30, h_tech = 5, kernel_per_image = FALSE, image_re = c("none", "intercept", "slopes", "condition_slopes"), types = NULL, resp_term = NULL, verbose = TRUE, ... )paceModelGaussian(object, ...) ## S4 method for signature 'SpatialExperiment' paceModelGaussian( object, celltype_col, image_col = NULL, condition_col = NULL, assay_name = "intensity", h_bio = 30, h_tech = 5, kernel_per_image = FALSE, image_re = c("none", "intercept", "slopes", "condition_slopes"), types = NULL, resp_term = NULL, verbose = TRUE, ... )
object |
|
... |
Passed to |
celltype_col |
Column of |
image_col |
Column of |
condition_col |
Optional column of |
assay_name |
The assay holding the intensities. |
h_bio, h_tech
|
Bandwidths of the biological and technical kernels. |
kernel_per_image |
Build the kernels within each image separately. |
image_re |
An optional second random-effect block over images. |
types |
Cell types, in the order the fit should use them. |
resp_term |
Optional response term passed to the model frame. |
verbose |
Print the fitting trace. |
The working response on the identity link is the intensity itself and the working weight is the per-gene residual variance, so the mean converges in a single inner solve and the outer iteration only moves the variance components.
This path does not model technical contamination. Correct the intensities
first, against an ambient field built with ambientField(), and pass the
corrected matrix here.
A PACEFit.
The settings the fit actually used, rather than the ones requested: kernel
bandwidths, the contamination and dispersion models, the column names read
from the SpatialExperiment, and the condition term if there is one. Worth
reading before comparing two fits, since several of these change the answer.
paceParams(object, ...) ## S4 method for signature 'PACEFit' paceParams(object, ...)paceParams(object, ...) ## S4 method for signature 'PACEFit' paceParams(object, ...)
object |
A PACEFit. |
... |
Unused. |
A named list of parameters.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) paceParams(fit)$h_biofit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) paceParams(fit)$h_bio
Stabilises the fitted per-(gene, focal, neighbour) proximity slopes with
multivariate adaptive shrinkage, populating neighbourSlopes().
paceShrink(object, ...) ## S4 method for signature 'PACEFit' paceShrink(object, ...)paceShrink(object, ...) ## S4 method for signature 'PACEFit' paceShrink(object, ...)
object |
A PACEFit from |
... |
Further arguments passed to the shrinkage step, notably
It is not, however, the serial computation. With |
One mash model is fitted per neighbour cell type, and every one of them is fitted over the same genes. mash calibrates against the genes it is given, so a common gene set keeps the lfsr comparable across neighbours; a gene that cannot be used for one neighbour is dropped for all of them, with a message.
The PACEFit with the shrunken neighbour slopes added.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit <- paceShrink(fit) head(neighbourSlopes(fit))fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) fit <- paceShrink(fit) head(neighbourSlopes(fit))
Returns the tidy per-(focal, neighbour) spatial variance table that
plotPairHeatmap() draws: for each focal cell type, the percentage of its
total expression variance contributed by spatial interaction with each
neighbour, obtained by splitting the focal's spatial share across neighbours
by a normalised (off-diagonal-renormalised) Pratt attribution. Off-diagonal
pairs only.
pairVariance(object, ...) ## S4 method for signature 'PACEFit' pairVariance(object, block = c("spatial", "responder"), ...)pairVariance(object, ...) ## S4 method for signature 'PACEFit' pairVariance(object, block = c("spatial", "responder"), ...)
object |
A PACEFit. |
... |
Unused. |
block |
Either |
A data frame with columns focal, neighbour, and val (spatial %
of the focal type's total variance).
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) head(pairVariance(fit))fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) head(pairVariance(fit))
Reproduces the manuscript per-focal decomposition figure: a full stacked bar of the cell-type identity, spatial cell state, and contamination (spillover) components, with a zoomed grouped bar of the two small spatial components.
plotDecomposition(object, title = "Per-focal decomposition")plotDecomposition(object, title = "Per-focal decomposition")
object |
A PACEFit. |
title |
Plot title. |
A patchwork / ggplot object.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) plotDecomposition(fit)fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) plotDecomposition(fit)
Reproduces the manuscript per-pair driver figure: the top genes ranked by
their driver score for a focal-neighbour pair, alongside their per-gene
single-frame variance decomposition. The score is carried in the
driver_score column of topDrivers(object).
plotDrivers( object, focal, neighbour, n_top = 5, panels = c("both", "score", "gene") )plotDrivers( object, focal, neighbour, n_top = 5, panels = c("both", "score", "gene") )
object |
A PACEFit. |
focal, neighbour
|
The focal and neighbour cell types. |
n_top |
Number of top genes to show. |
panels |
One of |
A patchwork / ggplot object.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) plotDrivers(fit, "Macrophage", "Tumour")fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) plotDrivers(fit, "Macrophage", "Tumour")
Reproduces the manuscript pairwise heatmap: each cell is the percentage of the focal type's total variance contributed by spatial interaction with that neighbour, computed as the focal's spatial share split across neighbours by the off-diagonal-renormalised Pratt attribution.
plotPairHeatmap(object, block = c("spatial", "responder"), title = NULL)plotPairHeatmap(object, block = c("spatial", "responder"), title = NULL)
object |
A PACEFit. |
block |
Which spatial block to map: |
title |
Plot title; a sensible default is chosen per |
A ggplot object.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) plotPairHeatmap(fit)fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) plotPairHeatmap(fit)
Reproduces the manuscript proximity figure (verbatim style port of
density_bin_boxplot in the analysis helpers): for each focal cell the number
of neighbour-type cells within radius micrometres is counted and binned,
and each gene's raw counts are drawn as a boxplot per bin with the per-bin mean
overlaid as a point. Because these genes are zero-inflated the box median sits
at zero in most bins, so the mean point carries the trend.
plotProximity( object, spe, genes, focal, neighbour, radius = 30, breaks = c(0, 2, 4, 6, 8, Inf), condition = NULL, condition_colours = c("#3B6FB6", "#C0392B"), box_colour = "#4F8B5E", assay_name = "counts" )plotProximity( object, spe, genes, focal, neighbour, radius = 30, breaks = c(0, 2, 4, 6, 8, Inf), condition = NULL, condition_colours = c("#3B6FB6", "#C0392B"), box_colour = "#4F8B5E", assay_name = "counts" )
object |
A PACEFit (supplies the cell-type column used at fit time). |
spe |
The SpatialExperiment::SpatialExperiment that was fitted. |
genes |
Character vector of genes (shown as facets). |
focal, neighbour
|
Focal and neighbour cell types. |
radius |
Neighbour search radius in micrometres (default 30). |
breaks |
Neighbour-count bin breaks (default |
condition |
Optional |
condition_colours |
Colours for the condition levels. |
box_colour |
Box and mean-point colour, used when |
assay_name |
Counts assay name (default |
A ggplot object.
spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) g <- head(subset(neighbourSlopes(fit), focal == "Macrophage" & neighbour == "Tumour")$gene, 2) plotProximity(fit, spe, g, "Macrophage", "Tumour")spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) g <- head(subset(neighbourSlopes(fit), focal == "Macrophage" & neighbour == "Tumour")$gene, 2) plotProximity(fit, spe, g, "Macrophage", "Tumour")
Population view of one gene in focal cells against neighbour density,
split by the fit's condition. The solid line and ribbon are binned means and
standard errors of the observed data; the dashed line is the PACE slope read
straight from the fitted model, anchored at each arm's data centroid. It is
the model's estimate drawn over the data, not a smoother refitted to it.
plotResponseCurve( object, spe, gene, focal, neighbour, n_bins = 10, min_bin_n = 10, colours = c("#3B6FB6", "#C0392B") )plotResponseCurve( object, spe, gene, focal, neighbour, n_bins = 10, min_bin_n = 10, colours = c("#3B6FB6", "#C0392B") )
object |
A PACEFit fitted with a |
spe |
The SpatialExperiment::SpatialExperiment that was fitted. |
gene, focal, neighbour
|
Gene, focal cell type and neighbour cell type. |
n_bins |
Number of density quantile bins (default 10). |
min_bin_n |
Bins with fewer focal cells than this are dropped. |
colours |
Colours for the two arms. |
A ggplot object.
spe <- readRDS(system.file("extdata", "mel_cosmx_subset.rds", package = "PACE")) fit <- paceFit(spe, celltype_col = "cellType", condition_col = "Responder", image_col = "image", kernel_per_image = TRUE, image_re = "intercept", verbose = FALSE) plotResponseCurve(fit, spe, "SPP1", "Macrophage", "Tumour")spe <- readRDS(system.file("extdata", "mel_cosmx_subset.rds", package = "PACE")) fit <- paceFit(spe, celltype_col = "cellType", condition_col = "Responder", image_col = "image", kernel_per_image = TRUE, image_re = "intercept", verbose = FALSE) plotResponseCurve(fit, spe, "SPP1", "Macrophage", "Tumour")
The spatial view behind plotResponseCurve(). For one image per condition
arm: a smooth kernel-density map of the neighbour type with the focal cells
overlaid and coloured by expression (top), and expression against neighbour
density for the same cells with a fitted line (bottom). Colour scales are
shared across the panels so the arms are directly comparable.
plotResponseMap( object, spe, gene, focal, neighbour, images = NULL, kde_h = 60, ngrid = 200, scalebar_len = 100 )plotResponseMap( object, spe, gene, focal, neighbour, images = NULL, kde_h = 60, ngrid = 200, scalebar_len = 100 )
object |
A PACEFit fitted with a |
spe |
The SpatialExperiment::SpatialExperiment that was fitted. |
gene, focal, neighbour
|
Gene, focal cell type and neighbour cell type. |
images |
Optional named character vector of image identifiers, one per arm. The default picks, for each arm, the image with the most focal cells. |
kde_h, ngrid
|
Kernel bandwidth and grid size for the density map. |
scalebar_len |
Scale-bar length in micrometres. |
A patchwork object.
spe <- readRDS(system.file("extdata", "mel_cosmx_subset.rds", package = "PACE")) fit <- paceFit(spe, celltype_col = "cellType", condition_col = "Responder", image_col = "image", kernel_per_image = TRUE, image_re = "intercept", verbose = FALSE) plotResponseMap(fit, spe, "SPP1", "Macrophage", "Tumour")spe <- readRDS(system.file("extdata", "mel_cosmx_subset.rds", package = "PACE")) fit <- paceFit(spe, celltype_col = "cellType", condition_col = "Responder", image_col = "image", kernel_per_image = TRUE, image_re = "intercept", verbose = FALSE) plotResponseMap(fit, spe, "SPP1", "Macrophage", "Tumour")
Copies a fit's interpretable output into metadata(spe), so that results
travel with the data they came from and downstream code can find them
without being handed a second object.
stashPACE(spe, fit, name = "PACE", overwrite = FALSE)stashPACE(spe, fit, name = "PACE", overwrite = FALSE)
spe |
A SpatialExperiment::SpatialExperiment – the one the fit was built on. The cell count is checked against the fit. |
fit |
A PACEFit. |
name |
The |
overwrite |
Whether to replace an existing element of that name. |
The solver state is deliberately not copied. A fit is large, it is often
saved and reloaded independently of the data, and nothing downstream reads
it; see fitState() if you need it. What is stashed is the three reporting
layers plus enough provenance to tell which fit produced them: the cell
types, the fitting parameters, and the package version.
spe, with metadata(spe)[[name]] populated.
neighbourSlopes(), varianceDecomposition(), topDrivers()
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) spe <- stashPACE(spe, fit) names(S4Vectors::metadata(spe)$PACE) head(S4Vectors::metadata(spe)$PACE$neighbourSlopes, 3)fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) spe <- readRDS(system.file("extdata", "bc_xenium_subset.rds", package = "PACE")) spe <- stashPACE(spe, fit) names(S4Vectors::metadata(spe)$PACE) head(S4Vectors::metadata(spe)$PACE$neighbourSlopes, 3)
Returns the driver-score tables ranking the genes that mediate each focal-neighbour spatial relationship.
topDrivers(object, ...) topDrivers(object) <- value ## S4 method for signature 'PACEFit' topDrivers(object, ...) ## S4 replacement method for signature 'PACEFit' topDrivers(object) <- valuetopDrivers(object, ...) topDrivers(object) <- value ## S4 method for signature 'PACEFit' topDrivers(object, ...) ## S4 replacement method for signature 'PACEFit' topDrivers(object) <- value
object |
A PACEFit. |
... |
Unused. |
value |
A named list of per-pair driver tables, each with |
Each pair's genes are called at lfsr < 0.05. The lfsr is the posterior
probability that a call has the wrong sign, so its sum over a pair's calls,
expected_false_sign, is the expected number of those calls with the wrong
sign, and false_sign_rate is that count as a fraction of the calls
(Stephens 2017). Read a pair's call count against it: many ordered pairs are
tested, and a pair with three calls and an expected 1.5 wrong is weak
evidence. Both are only as well calibrated as the lfsr itself.
On a fit with a condition, the table carries two different slopes and they
are not a raw-and-shrunken pair of the same quantity. b_clean and lfsr
are the condition-interaction term, the one the shrinkage was applied to, and
the driver score is built from b_clean. u_raw is the baseline
slope, the unshrunken BLUP for the neighbour on its own, and it is there
because the variance split needs it: V_S comes from u_raw and V_RxS
from b_clean. On a fit without a condition there is no interaction to
separate, so u_raw is absent and b_clean is the only slope.
A named list with one entry per focal-neighbour pair, each a list
with scores (the driver table), status, expected_false_sign and
false_sign_rate.
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) names(topDrivers(fit))fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) names(topDrivers(fit))
Returns the per-gene, per-focal variance decomposition. By default the
observed single-frame decomposition (cell-type identity, spatial cell state,
contamination, residual; and a responder spatial block for condition cohorts)
is returned; set which = "blocks" for the underlying link-scale block tables.
varianceDecomposition(object, ...) varianceDecomposition(object) <- value ## S4 method for signature 'PACEFit' varianceDecomposition(object, which = c("perGene", "blocks"), ...) ## S4 replacement method for signature 'PACEFit' varianceDecomposition(object) <- valuevarianceDecomposition(object, ...) varianceDecomposition(object) <- value ## S4 method for signature 'PACEFit' varianceDecomposition(object, which = c("perGene", "blocks"), ...) ## S4 replacement method for signature 'PACEFit' varianceDecomposition(object) <- value
object |
A PACEFit. |
... |
Unused. |
value |
Either the per-gene table, as the getter returns by default, or
the whole list with its |
which |
Either |
A data frame (for "perGene") or a list (for "blocks").
fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) head(varianceDecomposition(fit))fit <- readRDS(system.file("extdata", "pace_fit_example.rds", package = "PACE")) head(varianceDecomposition(fit))