| Title: | Self-Adaptive Spatial Tweedie Models for Spatially Variable Gene Detection |
|---|---|
| Description: | TweedSpot detects spatially variable genes (SVGs) in spatially resolved transcriptomics data using self-adaptive Tweedie generalized additive models with nonparametric spatial smoothing. The method models raw counts directly, avoiding the bias introduced by log-transformation, adapts a gene-specific Tweedie index parameter to the mean-variance relationship of each gene, and offers an optional two-part formulation to accommodate excess zeros. Inputs and outputs use the SpatialExperiment infrastructure for interoperability with the Bioconductor ecosystem. |
| Authors: | Nalin Arora [aut, cre] (affiliation: Cornell University, ORCID: <https://orcid.org/0009-0009-1340-688X>), Himel Mallick [aut] (affiliation: Cornell University), Vivian Myers [aut] |
| Maintainer: | Nalin Arora <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.99.3 |
| Built: | 2026-10-14 18:56:11 UTC |
| Source: | https://github.com/BiocStaging/TweedSpot |
filter_features() prepares a spatial dataset for tweedspot(). It removes
(or flags) features with too little signal to model, features that cannot
support the spatial smooth TweedSpot will fit, named features such as
control probes, and low-quality spatial locations. All criteria are
modality agnostic and apply to genes, proteins, peaks, or metabolites, and
to spots, cells, or pixels; mitochondrial and ribosomal gene removal is
available as a transcriptomics-specific option.
filter_features( input, assay_name = "counts", min_total = 5, min_pct_locations = 5, min_locations = 0, min_mean = 0, min_var = 0, detection_threshold = 0, model_aware = TRUE, smooth_k = NULL, group = NULL, min_group_size = 50, two_part = FALSE, exclude = NULL, exclude_pattern = NULL, feature_name_col = NULL, exclude_mito = FALSE, exclude_ribo = FALSE, feature_type_col = NULL, min_total_per_location = 0, min_features_per_location = 0, outlier_nmads = NULL, max_excluded_fraction = NULL, location_mask = NULL, drop_empty_locations = TRUE, action = c("drop", "flag"), report = FALSE )filter_features( input, assay_name = "counts", min_total = 5, min_pct_locations = 5, min_locations = 0, min_mean = 0, min_var = 0, detection_threshold = 0, model_aware = TRUE, smooth_k = NULL, group = NULL, min_group_size = 50, two_part = FALSE, exclude = NULL, exclude_pattern = NULL, feature_name_col = NULL, exclude_mito = FALSE, exclude_ribo = FALSE, feature_type_col = NULL, min_total_per_location = 0, min_features_per_location = 0, outlier_nmads = NULL, max_excluded_fraction = NULL, location_mask = NULL, drop_empty_locations = TRUE, action = c("drop", "flag"), report = FALSE )
input |
A SpatialExperiment::SpatialExperiment with a non-negative feature-by-location assay. |
assay_name |
Name of the assay to filter. Default |
min_total |
Minimum total signal (sum across locations) for a feature.
Default |
min_pct_locations |
Minimum percent of locations in which the feature
is detected. Default |
min_locations |
Minimum number of locations in which the feature is
detected. Default |
min_mean |
Minimum mean signal across locations. Default |
min_var |
Minimum variance across locations. Default |
detection_threshold |
A feature counts as detected at a location when
its value is strictly greater than this. Default |
model_aware |
Logical; require each feature to be detected in at least
as many locations as the spatial smooth has basis functions (see
Details). Default |
smooth_k |
Basis dimension of the spatial smooth, as passed to
|
group |
Optional name of a discrete |
min_group_size |
Groups with fewer locations than this are ignored when
|
two_part |
Logical; whether you will fit |
exclude |
Optional character vector of feature names to remove, matched
exactly against row names and, if supplied, |
exclude_pattern |
Optional character vector of regular expressions;
features whose row name or |
feature_name_col |
Optional |
exclude_mito |
Logical; transcriptomics only. Remove mitochondrial
genes (names beginning |
exclude_ribo |
Logical; transcriptomics only. Remove ribosomal protein
genes ( |
feature_type_col |
Optional |
min_total_per_location |
Minimum total signal for a location. Default
|
min_features_per_location |
Minimum number of features detected at a
location. Default |
outlier_nmads |
Optional number of median absolute deviations; a
location is removed when its log total signal or log number of detected
features falls more than this many MADs below the median. Default |
max_excluded_fraction |
Optional maximum fraction of a location's
signal that may come from excluded features (for example |
location_mask |
Optional name of a logical |
drop_empty_locations |
Logical; remove locations with no signal after
feature filtering. Default |
action |
|
report |
Logical; print how many features and locations were kept and
how many failed each criterion. Default |
Filtering happens in this order:
Named exclusions (exclude, exclude_pattern, exclude_mito,
exclude_ribo) mark features to remove.
Location QC is computed on the remaining features: minimum total signal, minimum detected features, lower-tail outliers, the fraction of signal from excluded features, and an optional mask.
Feature thresholds are computed on the locations that pass QC, including the model-aware minimum below.
Locations with no remaining signal are removed (drop_empty_locations).
Model-aware minimum. With model_aware = TRUE, a feature must be
detected in at least as many locations as the spatial smooth has basis
functions (smooth_k, or TweedSpot's default of up to 30). Features below
this cannot support the smooth and would otherwise fail or give unstable
fits. Pass the same smooth_k you will give tweedspot(). With group,
the minimum is checked within each group using that group's locations, and
a feature is kept if at least one group can support it; the per-group
result is stored in metadata(output)$filter_features$group_support. For the
two-part model the presence/absence part additionally needs that many
detected and undetected locations; features that fail only this are kept,
and tweedspot() then tests them with the count part alone.
All feature criteria depend only on abundance and detection, not on spatial arrangement, so filtering does not bias the spatial test's p-values.
A SpatialExperiment. With action = "drop", only retained
features and locations; with action = "flag", all of them with
filter_keep and filter_reason columns. In both cases
S4Vectors::metadata(output)$filter_features records the settings and the
number of features and locations failing each criterion.
set.seed(1) counts <- matrix(stats::rpois(5 * 36, 4), nrow = 5) counts[2, ] <- 0 counts[2, 1:2] <- 3 rownames(counts) <- c("GENE1", "RARE1", "MT-CO1", "NegPrb1", "GENE5") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), spatialCoords = as.matrix(expand.grid(x = 1:6, y = 1:6)) ) # Drop features, using a smooth with 10 basis functions filter_features(spe, smooth_k = 10, exclude_pattern = "^NegPrb") # Keep everything but record why each feature or location would be removed flagged <- filter_features(spe, smooth_k = 10, exclude_mito = TRUE, max_excluded_fraction = 0.3, action = "flag") SummarizedExperiment::rowData(flagged)[, c("filter_keep", "filter_reason")]set.seed(1) counts <- matrix(stats::rpois(5 * 36, 4), nrow = 5) counts[2, ] <- 0 counts[2, 1:2] <- 3 rownames(counts) <- c("GENE1", "RARE1", "MT-CO1", "NegPrb1", "GENE5") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), spatialCoords = as.matrix(expand.grid(x = 1:6, y = 1:6)) ) # Drop features, using a smooth with 10 basis functions filter_features(spe, smooth_k = 10, exclude_pattern = "^NegPrb") # Keep everything but record why each feature or location would be removed flagged <- filter_features(spe, smooth_k = 10, exclude_mito = TRUE, max_excluded_fraction = 0.3, action = "flag") SummarizedExperiment::rowData(flagged)[, c("filter_keep", "filter_reason")]
get_tweedspot_results() pulls the TweedSpot result columns from
rowData(input) into a base data.frame and can optionally sort the output.
get_tweedspot_results( input, sort_by = c("padj", "none", "pval", "stat", "dev_expl"), decreasing = NULL, group = NULL )get_tweedspot_results( input, sort_by = c("padj", "none", "pval", "stat", "dev_expl"), decreasing = NULL, group = NULL )
input |
A SpatialExperiment::SpatialExperiment containing TweedSpot
results in |
sort_by |
Column to sort by. Options are |
decreasing |
Logical; override the default sort direction for
|
group |
For results of |
A data.frame. Without group, one row per feature with columns
feature (row name), feature_name (when rowData() has a
feature_name or gene_name column that differs from the row names),
stat (test statistic of the spatial smooth), pval, padj, edf
(effective degrees of freedom of the smooth), dev_expl (deviance
explained), and, when available, p (Tweedie variance power) and
family. With group, one row per feature and group with columns feature, group, n_locations, n_detected,
tested, reason (why a pair was not tested), stat, pval, padj,
padj_feature (stage-wise screening p-value), edf, dev_expl, p, and
family.
counts <- matrix(c(1, 2, 3, 4, 2, 2), nrow = 2, byrow = TRUE) rownames(counts) <- c("gene1", "gene2") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), rowData = S4Vectors::DataFrame( gene_id = rownames(counts), gene_name = rownames(counts), tweedspot_stat = c(4, 2), tweedspot_pval = c(0.01, 0.05), tweedspot_padj = c(0.02, 0.05), tweedspot_edf = c(2, 2), tweedspot_dev_expl = c(0.3, 0.1) ), colData = S4Vectors::DataFrame(sample_id = rep("sample1", ncol(counts))), spatialCoords = cbind(x = seq_len(ncol(counts)), y = seq_len(ncol(counts))) ) get_tweedspot_results(spe)counts <- matrix(c(1, 2, 3, 4, 2, 2), nrow = 2, byrow = TRUE) rownames(counts) <- c("gene1", "gene2") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), rowData = S4Vectors::DataFrame( gene_id = rownames(counts), gene_name = rownames(counts), tweedspot_stat = c(4, 2), tweedspot_pval = c(0.01, 0.05), tweedspot_padj = c(0.02, 0.05), tweedspot_edf = c(2, 2), tweedspot_dev_expl = c(0.3, 0.1) ), colData = S4Vectors::DataFrame(sample_id = rep("sample1", ncol(counts))), spatialCoords = cbind(x = seq_len(ncol(counts)), y = seq_len(ncol(counts))) ) get_tweedspot_results(spe)
plot_feature() refits the TweedSpot model for one feature and draws three
aligned panels: the observed normalized signal, the fitted mean from the
TweedSpot model on the same colour scale, and the fitted spatial effect as a
log2 fold change relative to the feature's average. The model is refitted
with the settings stored by tweedspot() in
S4Vectors::metadata(input)$tweedspot, so the figure shows exactly the model
that was tested; any of those settings can be overridden through ....
plot_feature( input, feature, group = NULL, transform = c("log1p", "none"), flip_y = TRUE, point_size = NULL, base_size = 8, base_family = "", output_dir = NULL, formats = "pdf", width = 7.2, height = 2.9, dpi = 300, ... )plot_feature( input, feature, group = NULL, transform = c("log1p", "none"), flip_y = TRUE, point_size = NULL, base_size = 8, base_family = "", output_dir = NULL, formats = "pdf", width = 7.2, height = 2.9, dpi = 300, ... )
input |
A SpatialExperiment::SpatialExperiment, typically the output
of |
feature |
A single row index, row name, or value of
|
group |
For results of |
transform |
Display transform for the observed and fitted panels:
|
flip_y |
Logical; reverse the y axis so that images with the origin at
the top-left are drawn upright. Default |
point_size |
Size of the location markers. Default |
base_size |
Base font size in points. Default |
base_family |
Font family. Default |
output_dir |
Optional directory in which to save the figure. It is
created if it does not exist. Default |
formats |
File formats to write when |
width, height
|
Figure size in inches, used for saving and for choosing
|
dpi |
Resolution for raster formats. Default |
... |
Model settings that override those stored by |
For a two-part run (two_part = TRUE) the panels show the count
(abundance) part of the model.
A patchwork object. When output_dir is supplied, the paths of the
written files are attached as attr(, "files").
if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("patchwork", quietly = TRUE)) { set.seed(1) grid <- expand.grid(x = 1:12, y = 1:12) counts <- rbind( band = stats::rpois(144, ifelse(grid$y > 6, 12, 2)), flat = stats::rpois(144, 5) ) colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), spatialCoords = as.matrix(grid) ) spe <- tweedspot(spe, smooth_k = 10, size_factor_method = "library_size", BPPARAM = BiocParallel::SerialParam(), verbose = FALSE) plot_feature(spe, "band") }if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("patchwork", quietly = TRUE)) { set.seed(1) grid <- expand.grid(x = 1:12, y = 1:12) counts <- rbind( band = stats::rpois(144, ifelse(grid$y > 6, 12, 2)), flat = stats::rpois(144, 5) ) colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), spatialCoords = as.matrix(grid) ) spe <- tweedspot(spe, smooth_k = 10, size_factor_method = "library_size", BPPARAM = BiocParallel::SerialParam(), verbose = FALSE) plot_feature(spe, "band") }
plot_top_features() draws the fitted spatial effect of the most
significant features from a tweedspot() run as small multiples on one
shared diverging scale, so that the strength of different patterns can be
compared directly. Each panel is labelled with the feature name and, by
default, its adjusted p-value and deviance explained.
plot_top_features( input, n_top = 6, features = NULL, group = NULL, ncol = NULL, label_stats = TRUE, title = NULL, flip_y = TRUE, point_size = NULL, base_size = 8, base_family = "", output_dir = NULL, formats = "pdf", width = 7.2, height = NULL, dpi = 300, ... )plot_top_features( input, n_top = 6, features = NULL, group = NULL, ncol = NULL, label_stats = TRUE, title = NULL, flip_y = TRUE, point_size = NULL, base_size = 8, base_family = "", output_dir = NULL, formats = "pdf", width = 7.2, height = NULL, dpi = 300, ... )
input |
A SpatialExperiment::SpatialExperiment, typically the output
of |
n_top |
Number of top features to show, ranked by adjusted p-value and
then by test statistic. Default |
features |
Optional vector of features (row indices, row names, or
feature names) to show instead of the top |
group |
For results of |
ncol |
Number of panel columns. Default |
label_stats |
Logical; add the adjusted p-value and deviance explained
under each feature name. Default |
title |
Optional figure title. Default |
flip_y |
Logical; reverse the y axis so that images with the origin at
the top-left are drawn upright. Default |
point_size |
Size of the location markers. Default |
base_size |
Base font size in points. Default |
base_family |
Font family. Default |
output_dir |
Optional directory in which to save the figure. It is
created if it does not exist. Default |
formats |
File formats to write when |
width |
Figure width in inches, used for saving and for choosing
|
height |
Figure height in inches. Default |
dpi |
Resolution for raster formats. Default |
... |
Model settings that override those stored by |
A ggplot object. When output_dir is supplied, the paths of the
written files are attached as attr(, "files").
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(1) grid <- expand.grid(x = 1:12, y = 1:12) counts <- rbind( band = stats::rpois(144, ifelse(grid$y > 6, 12, 2)), spot = stats::rpois(144, 2 + 10 * exp(-((grid$x - 4)^2 + (grid$y - 4)^2) / 6)), flat = stats::rpois(144, 5) ) colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), spatialCoords = as.matrix(grid) ) spe <- tweedspot(spe, smooth_k = 10, size_factor_method = "library_size", BPPARAM = BiocParallel::SerialParam(), verbose = FALSE) plot_top_features(spe, n_top = 2) }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(1) grid <- expand.grid(x = 1:12, y = 1:12) counts <- rbind( band = stats::rpois(144, ifelse(grid$y > 6, 12, 2)), spot = stats::rpois(144, 2 + 10 * exp(-((grid$x - 4)^2 + (grid$y - 4)^2) / 6)), flat = stats::rpois(144, 5) ) colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), spatialCoords = as.matrix(grid) ) spe <- tweedspot(spe, smooth_k = 10, size_factor_method = "library_size", BPPARAM = BiocParallel::SerialParam(), verbose = FALSE) plot_top_features(spe, n_top = 2) }
top_spatial_genes() returns the top n rows from
get_tweedspot_results() using a user-selected ranking metric.
top_spatial_genes( input, n = 10L, rank_by = c("padj", "pval", "stat", "dev_expl"), group = NULL )top_spatial_genes( input, n = 10L, rank_by = c("padj", "pval", "stat", "dev_expl"), group = NULL )
input |
A SpatialExperiment::SpatialExperiment containing TweedSpot
results in |
n |
Number of rows to return. Default |
rank_by |
Ranking column. Options are |
group |
For results of |
A ranked data.frame containing the top TweedSpot hits.
counts <- matrix(c(1, 2, 3, 4, 2, 2), nrow = 2, byrow = TRUE) rownames(counts) <- c("gene1", "gene2") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), rowData = S4Vectors::DataFrame( gene_id = rownames(counts), gene_name = rownames(counts), tweedspot_stat = c(4, 2), tweedspot_pval = c(0.01, 0.05), tweedspot_padj = c(0.02, 0.05), tweedspot_edf = c(2, 2), tweedspot_dev_expl = c(0.3, 0.1) ), colData = S4Vectors::DataFrame(sample_id = rep("sample1", ncol(counts))), spatialCoords = cbind(x = seq_len(ncol(counts)), y = seq_len(ncol(counts))) ) top_spatial_genes(spe, n = 1)counts <- matrix(c(1, 2, 3, 4, 2, 2), nrow = 2, byrow = TRUE) rownames(counts) <- c("gene1", "gene2") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), rowData = S4Vectors::DataFrame( gene_id = rownames(counts), gene_name = rownames(counts), tweedspot_stat = c(4, 2), tweedspot_pval = c(0.01, 0.05), tweedspot_padj = c(0.02, 0.05), tweedspot_edf = c(2, 2), tweedspot_dev_expl = c(0.3, 0.1) ), colData = S4Vectors::DataFrame(sample_id = rep("sample1", ncol(counts))), spatialCoords = cbind(x = seq_len(ncol(counts)), y = seq_len(ncol(counts))) ) top_spatial_genes(spe, n = 1)
tweedspot() fits a Tweedie GAM with a spatial smooth s(x, y) per gene
and tests each gene for overall spatial variation.
tweedspot( input, assay_name = "counts", covariates = NULL, two_part = FALSE, family = "tw", tweedie_p = NULL, fit_method = "REML", use_bam = FALSE, bam_discrete = TRUE, bam_nthreads = 1L, smooth_k = NULL, size_factor_method = c("scran", "library_size", "spanorm"), spanorm_output = c("size_factor", "counts"), spanorm_args = list(), p_criterion = c("BIC", "AIC"), padj_method = "BY", group = NULL, min_group_size = 50, group_adjust = c("stagewise", "global", "per_group"), alpha = 0.05, seed = NULL, BPPARAM = BiocParallel::bpparam(), verbose = TRUE )tweedspot( input, assay_name = "counts", covariates = NULL, two_part = FALSE, family = "tw", tweedie_p = NULL, fit_method = "REML", use_bam = FALSE, bam_discrete = TRUE, bam_nthreads = 1L, smooth_k = NULL, size_factor_method = c("scran", "library_size", "spanorm"), spanorm_output = c("size_factor", "counts"), spanorm_args = list(), p_criterion = c("BIC", "AIC"), padj_method = "BY", group = NULL, min_group_size = 50, group_adjust = c("stagewise", "global", "per_group"), alpha = 0.05, seed = NULL, BPPARAM = BiocParallel::bpparam(), verbose = TRUE )
input |
A SpatialExperiment::SpatialExperiment with a count assay and spatial coordinates. |
assay_name |
Name of the count assay to model. Default |
covariates |
Optional one-sided formula specifying columns from
|
two_part |
Logical; if |
family |
|
tweedie_p |
Tweedie variance power |
fit_method |
Smoothing parameter estimation method passed to |
use_bam |
Logical; use |
bam_discrete |
Logical; when |
bam_nthreads |
Integer; number of threads for |
smooth_k |
Optional basis dimension for the spatial smooth. Smaller
values can speed fitting at the cost of flexibility. Default |
size_factor_method |
Method used to compute the size-factor offset.
|
spanorm_output |
How the SpaNorm fit enters TweedSpot when
|
spanorm_args |
Optional named list of additional arguments passed to
|
p_criterion |
Information criterion used when |
padj_method |
Multiple-testing correction passed to |
group |
Optional name of a discrete |
min_group_size |
Groups with fewer locations than this are not tested.
Default |
group_adjust |
Multiple-testing adjustment across features and groups:
|
alpha |
Target significance level for group results: used by the
stage-wise adjustment and to summarise |
seed |
Optional whole number. When given, the random-number generator
is seeded with it for the duration of the call (and restored afterwards),
so steps that use random numbers, such as SpaNorm's subsampling of
locations, give identical results on every run. The seed is stored and
reused by |
BPPARAM |
A BiocParallel::BiocParallelParam object controlling
parallelization across genes. Default |
verbose |
Logical; if |
Normalization. TweedSpot models raw counts with a log-link GAM and a
size-factor offset, so only the relative size factors across spots matter.
scuttle::logNormCounts() is not offered as a separate option because,
without user-supplied size factors, it uses library-size factors
(colSums / mean(colSums)) that differ from "library_size" only by a
global constant, which the model intercept absorbs; with existing size
factors it reuses them. Its log-transformed output is also incompatible with
modelling raw counts. "spanorm" (Salim et al., 2025, Genome Biology)
estimates a spatially smooth, gene-specific library-size effect jointly with
spatial biology, without domain or cluster labels. TweedSpot treats that
estimated effect as known, so any error in separating library size from
biology remains in the data as spatial structure. When library size is
itself spatially structured, this can make the spatial test strongly
anti-conservative (in simulations, most null genes were called
significant). Use "spanorm" as an exploratory option and check its
calibration on your data; "scran" remains the default. SpaNorm is specific
to spatial transcriptomics count data; "scran" and "library_size" are
the modality-agnostic options.
SpaNorm fits, per gene, a negative binomial model whose log mean is
,
where is spatial biology and the second term is a spatially
varying library-size effect. With spanorm_output = "size_factor",
TweedSpot models raw counts and uses the library-size and batch part of
this predictor as the offset, , so the spatial biology is
tested by the TweedSpot smooth rather than removed or reused. With
spanorm_output = "counts", TweedSpot models SpaNorm's percentile-adjusted
counts, , which are non-negative integers with
the library-size effect removed, using a unit offset. The size-factor form
is the default because it keeps the original count likelihood.
Automatic variance power. With tweedie_p = "auto", each gene is
fitted separately under several candidate variance functions
: Gaussian (), Tweedie with
estimated in , and, when the gene has no zeros, Gamma
() and inverse Gaussian (). Every candidate shares the
log link, spatial smooth, covariates, and offset, and estimates its own
dispersion, so they differ only in the variance function. Poisson
() is not a candidate: its dispersion is fixed at 1, and choosing
it on the same fit that is tested inflates false positives for genes whose
noise happens to look spatial. It remains available as tweedie_p = 1. Each candidate is scored by
(or with
p_criterion = "AIC"), where is the maximized
log-likelihood and df the effective degrees of freedom from
mgcv::logLik.gam(): smooth EDF, plus one for an estimated scale, plus one
for an estimated Tweedie power. The candidate with the smallest value is used
for the spatial test, and its power is reported in
rowData(input)$tweedspot_p and rowData(input)$tweedspot_family. For
integer data the estimated Tweedie power is restricted to ,
because as the Tweedie density concentrates on spikes at the
integers, which inflates its likelihood and the spatial test.
Group-specific testing. With group = "cell_type", the locations of each
group are fitted separately with the same model (s(x, y), covariates,
offset, and family settings) to ask whether a feature varies in space among
locations of that group. A feature that differs between groups but is flat
within each group, a pure composition effect, is therefore not called
spatially variable. Size factors and the coordinate scaling are computed
once on all locations so that every group shares one scale; the spatial
basis size is capped by each group's number of locations; covariates that
are constant within a group are dropped for that group; and a feature is
tested in a group only if it is detected in at least as many of its
locations as the smooth has basis functions. Groups with fewer than
min_group_size locations are skipped.
Group p-values are adjusted with group_adjust. "stagewise" first screens
each feature with the Cauchy combination of its group p-values and adjusts
those across features with padj_method, then Holm-adjusts the group
p-values within the feature at the reduced level alpha * R / m (R of m
features pass the screen); a feature-group pair's adjusted p-value is the
larger of the screening and confirmation values, so it is significant at
alpha only if the feature passes the screen and the group passes
confirmation. Stage-wise adjusted p-values are calibrated for the alpha
used in the run. "global" adjusts all pairs
together and "per_group" adjusts within each group. The full table, one
row per feature and group, is stored in
S4Vectors::metadata(input)$tweedspot_group$results and returned by
get_tweedspot_results() with its group argument; rowData(input) gains
tweedspot_group_padj (feature-level adjusted p-value) and
tweedspot_group_sig (groups significant at alpha).
The input SpatialExperiment with per-gene statistic, p-value,
adjusted p-value, smooth effective degrees of freedom, deviance
explained, fitted variance power (tweedspot_p), and the corresponding
family (tweedspot_family) written to SummarizedExperiment::rowData().
The model settings are stored in S4Vectors::metadata(input)$tweedspot
and reused by plot_feature() and plot_top_features().
Lun ATL, Bach K, Marioni JC (2016). Pooling across cells to normalize single-cell RNA sequencing data with many zero counts. Genome Biology 17:75.
Salim A, Bhuva DD, Chen C, et al. (2025). SpaNorm: spatially-aware normalization for spatial transcriptomics data. Genome Biology.
counts <- matrix( c(0, 1, 2, 3, 4, 5, 5, 4, 5, 4, 3, 2), nrow = 2, byrow = TRUE ) rownames(counts) <- c("gene1", "gene2") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), rowData = S4Vectors::DataFrame( gene_id = rownames(counts), gene_name = rownames(counts) ), colData = S4Vectors::DataFrame(batch = rep(c("A", "B"), length.out = ncol(counts))), spatialCoords = cbind(x = seq_len(ncol(counts)), y = seq_len(ncol(counts))) ) spe <- tweedspot( spe, family = "poisson", smooth_k = 4, size_factor_method = "library_size", BPPARAM = BiocParallel::SerialParam() ) SummarizedExperiment::rowData(spe)[, c("tweedspot_stat", "tweedspot_pval")]counts <- matrix( c(0, 1, 2, 3, 4, 5, 5, 4, 5, 4, 3, 2), nrow = 2, byrow = TRUE ) rownames(counts) <- c("gene1", "gene2") colnames(counts) <- paste0("spot", seq_len(ncol(counts))) spe <- SpatialExperiment::SpatialExperiment( assays = list(counts = counts), rowData = S4Vectors::DataFrame( gene_id = rownames(counts), gene_name = rownames(counts) ), colData = S4Vectors::DataFrame(batch = rep(c("A", "B"), length.out = ncol(counts))), spatialCoords = cbind(x = seq_len(ncol(counts)), y = seq_len(ncol(counts))) ) spe <- tweedspot( spe, family = "poisson", smooth_k = 4, size_factor_method = "library_size", BPPARAM = BiocParallel::SerialParam() ) SummarizedExperiment::rowData(spe)[, c("tweedspot_stat", "tweedspot_pval")]