| Title: | Cell-Density Correlated Genes from Spatial Transcriptomics |
|---|---|
| Description: | Quantifies the dependence between local cell density and gene expression in single-cell resolution spatial transcriptomics data, such as data from the Xenium and MERFISH platforms. Cell locations form a point pattern and gene counts are its marks. After aggregation on a regular grid, cell counts and summed expression follow Poisson models driven by two coregionalized latent Gaussian fields with Matern covariance, approximated by Hilbert space basis functions; the correlation between the two fields measures the density-expression dependence. Models are fitted with 'Stan' through 'cmdstanr'. Functions cover the tissue boundary (an alpha shape of the cells), gridding, model fitting, posterior summaries, latent-field decomposition, spatial correlation ranges, and gene scans that call density-correlated genes. |
| Authors: | Mingchi Xu [aut], Alexandra M. Schmidt [aut], Qihuang Zhang [aut, cre] (ORCID: <https://orcid.org/0000-0003-1455-2159>) |
| Maintainer: | Qihuang Zhang <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.9.0 |
| Built: | 2026-10-25 22:52:16 UTC |
| Source: | https://github.com/StaGill/DenMark |
Each cell is assigned to exactly one grid cell (cells on an interior edge go to the upper or right cell; cells on the outer boundary are kept).
buildgridpp(grid_size, dataset, gene = NULL, bbox = NULL, boundary = NULL)buildgridpp(grid_size, dataset, gene = NULL, bbox = NULL, boundary = NULL)
grid_size |
Number of grid cells per axis, or |
dataset |
Data frame with columns |
gene |
Expression column to use (default: the first non-coordinate column). |
bbox |
Optional bounding box |
boundary |
Optional tissue polygon from |
A list. The vectors are ordered with x varying fastest, that is
grid_index = i + (j - 1) * nx, identical to as.vector(Ymat). The first
three elements keep the order of the original DenMark function, so
res[[1]], res[[2]] and res[[3]] still work.
loc_mark_mat |
N x 2 matrix with the columns |
Ymat |
nx x ny matrix of cell counts (rows = x, columns = y). |
Mmat |
nx x ny matrix of summed expression. |
grid |
Data frame with the columns |
bbox, step, grid_area, grid_size, gene, boundary
|
Bounding box,
grid steps |
toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) g <- buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA") dim(g$Ymat) head(g$grid) tissue <- denmark_alpha_shape(toy$x, toy$y) gb <- buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA", boundary = tissue) table(gb$grid$type)toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) g <- buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA") dim(g$Ymat) head(g$grid) tissue <- denmark_alpha_shape(toy$x, toy$y) gb <- buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA", boundary = tissue) table(gb$grid$type)
Grids the data, builds the HSGP inputs, compiles the Stan model (once per session), sets valid initial values and samples with cmdstanr.
DenMark( dataset, gene = NULL, grid_size = 10, basis = 25, boundfactor = 1.5, bbox = NULL, chains = 4, parallel_chains = chains, iter_warmup = 1000, iter_sampling = 1000, adapt_delta = 0.95, max_treedepth = 10, seed = 2024, refresh = 400, init = NULL, rho0 = 0.3, stan_file = NULL, output_dir = NULL, keep_fit = TRUE, quiet = FALSE, L1 = NULL, L2 = NULL, boundary = NULL, min_area_frac = 0, ... ) ## S3 method for class 'denmark' print(x, digits = 3, ...)DenMark( dataset, gene = NULL, grid_size = 10, basis = 25, boundfactor = 1.5, bbox = NULL, chains = 4, parallel_chains = chains, iter_warmup = 1000, iter_sampling = 1000, adapt_delta = 0.95, max_treedepth = 10, seed = 2024, refresh = 400, init = NULL, rho0 = 0.3, stan_file = NULL, output_dir = NULL, keep_fit = TRUE, quiet = FALSE, L1 = NULL, L2 = NULL, boundary = NULL, min_area_frac = 0, ... ) ## S3 method for class 'denmark' print(x, digits = 3, ...)
dataset |
Data frame with columns |
gene |
Name of the expression column (default: the first column other
than |
grid_size |
Grid cells per axis, or |
basis |
HSGP basis functions per axis, or |
boundfactor |
HSGP boundary factor (>= 1). |
bbox |
Optional region of interest |
chains, parallel_chains, iter_warmup, iter_sampling, adapt_delta, max_treedepth, seed, refresh
|
Passed to the cmdstanr |
init |
|
rho0 |
Starting value of rho. |
stan_file |
Optional path to a custom Stan file with the parameters
of |
output_dir |
Optional folder for the CmdStan CSV files (created if needed). |
keep_fit |
Keep the |
quiet |
Suppress the sampler progress output. |
L1, L2
|
Optional half-ranges of the region of interest; only checked
against the data (see |
boundary |
Optional tissue polygon from |
min_area_frac |
See |
... |
For |
x |
A |
digits |
Number of significant digits in the printed summary table. |
The DenMark model (DenMark.stan) has two dependent latent fields,
log lambda1(g) = log|g| + beta0 + a11 w1(g) and
log lambda2(g) = beta1 + log(Y(g) + 0.05) + a21 w1(g) + a22 w2(g);
rho = Astar[2,1] is the density-expression correlation.
A tissue section is rarely a rectangle. With boundary (an alpha shape of
all cells of the section, from denmark_alpha_shape()), the grid cells
outside the tissue are dropped and every remaining grid cell enters with
its area inside the tissue, through the variable-area model
DenMark-vararea.stan (the same model with one log_grid_area per grid
cell). Without boundary, the whole bounding box is gridded with a common
grid area.
The print method reports rho, the sampler diagnostics and the posterior summary.
An object of class denmark: a list with the cmdstanr fit
(fit), gene, stan_data, grid, info, the sampler settings,
the posterior summary (see denmark_summary()), the sampler
diagnostics, the run time in minutes, the HSGP check hsgp (see
denmark_check_hsgp()) and, for backward compatibility with
DenMarkmain(), rhat, Ymat and Mmat. With keep_fit = FALSE the
fit is dropped and the draws of the key parameters are kept in
draws_cache.
denmark_scan() to fit many genes.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) fittoy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) fit
The Delaunay triangulation of the cells is computed and every triangle
whose circumradius is smaller than alpha is kept; the union of the kept
triangles is the tissue region. This follows the boundary step of the
real-data analyses: compute it from all cells of the section (not only the
cell type analyzed), so that the density of one cell type is modeled over
the whole tissue rather than over its own convex hull.
denmark_alpha_shape( x, y, alpha = NULL, alpha_mult = 3, keep = c("largest", "all"), fill_holes = TRUE )denmark_alpha_shape( x, y, alpha = NULL, alpha_mult = 3, keep = c("largest", "all"), fill_holes = TRUE )
x, y
|
Cell coordinates (all cells of the section). |
alpha |
Circumradius threshold; the default is |
alpha_mult |
Multiplier for the default |
keep |
|
fill_holes |
Fill holes inside the tissue. |
An sfc polygon (package sf) with the attribute "alpha".
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) attr(tissue, "alpha") sf::st_area(tissue)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) attr(tissue, "alpha") sf::st_area(tissue)
Adds, for every feature of the assay, the posterior mean of rho, its
credible interval and the call from denmark_scan() to the feature
metadata (object[[assay]][[]]). Features that were not scanned get NA.
denmark_annotate(object, scan, assay = NULL, prefix = "denmark_")denmark_annotate(object, scan, assay = NULL, prefix = "denmark_")
object |
A Seurat object. |
scan |
Data frame returned by |
assay |
Assay whose feature metadata receive the results (default: the default assay). |
prefix |
Prefix of the new columns: |
The Seurat object with the new feature metadata.
counts <- Matrix::Matrix(matrix(rpois(30, 2), 3, 10, dimnames = list(c("geneA", "geneB", "geneC"), NULL)), sparse = TRUE) colnames(counts) <- paste0("cell", 1:10) obj <- SeuratObject::CreateSeuratObject(counts = counts) scan <- data.frame(gene = c("geneA", "geneC"), rho_mean = c(0.6, -0.6), rho_lower = c(0.3, -0.8), rho_upper = c(0.8, -0.3), class = c("PCG", "NCG")) obj <- denmark_annotate(obj, scan) obj[["RNA"]][[]]counts <- Matrix::Matrix(matrix(rpois(30, 2), 3, 10, dimnames = list(c("geneA", "geneB", "geneC"), NULL)), sparse = TRUE) colnames(counts) <- paste0("cell", 1:10) obj <- SeuratObject::CreateSeuratObject(counts = counts) scan <- data.frame(gene = c("geneA", "geneC"), rho_mean = c(0.6, -0.6), rho_lower = c(0.3, -0.8), rho_upper = c(0.8, -0.3), class = c("PCG", "NCG")) obj <- denmark_annotate(obj, scan) obj[["RNA"]][[]]
Uses the one-dimensional Matern-3/2 recommendations of Riutort-Mayol et
al. (2023, Statistics and Computing): with S the half-range of the
domain, c the boundary factor and m the number of basis functions, the
smallest length-scale that is well approximated is
ell_min = 3.42 * c * S / m, and the boundary factor should satisfy
c >= max(1.2, 4.5 * ell / S). Applied per axis; a message is issued when
the posterior median length-scale is below ell_min (increase basis)
or c is too small. The rule targets an accurate approximation of the
latent function itself and is conservative for estimating rho; treat it
as guidance.
denmark_check_hsgp(obj, warn = TRUE)denmark_check_hsgp(obj, warn = TRUE)
obj |
A |
warn |
Issue a message when the rule is not met. |
Data frame with one row per field and axis: field, axis,
ell_median, ell_min, c_needed, basis_ok and boundary_ok.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) denmark_check_hsgp(fit, warn = FALSE)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) denmark_check_hsgp(fit, warn = FALSE)
Density process: r1(d) = M(d; ell1). Expression process:
r2(d) = [a21^2 M(d; ell1) + a22^2 M(d; ell2)] / (a21^2 + a22^2), with M
the Matern-3/2 correlation. The practical range is the distance at which
the correlation falls to threshold (default 0.05), computed per
posterior draw.
denmark_decay(obj, distances = NULL, probs = c(0.025, 0.975), threshold = 0.05)denmark_decay(obj, distances = NULL, probs = c(0.025, 0.975), threshold = 0.05)
obj |
A |
distances |
Distances at which to evaluate the curves (data units); default: 200 points from 0 to the maximum distance between grid centroids. |
probs |
Lower and upper probabilities of the posterior band. |
threshold |
Correlation level that defines the practical range. |
A list with curves (data frame: field, distance, posterior mean,
lower and upper band), ranges (data frame: posterior mean, median and
interval of the practical range of each field), range_draws (the
practical ranges per draw) and threshold.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) decay <- denmark_decay(fit) decay$rangestoy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) decay <- denmark_decay(fit) decay$ranges
One row per posterior draw with the columns beta0, beta1, ell1,
ell2, rho, sigma1, sigma2 and the coregionalization coefficients
a11 = sigma1, a21 = sigma2 * rho and a22 = sigma2 * sqrt(1 - rho^2).
denmark_draws(obj)denmark_draws(obj)
obj |
A |
Data frame with one row per posterior draw.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) draws <- denmark_draws(fit) quantile(draws$rho, c(0.025, 0.5, 0.975))toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) draws <- denmark_draws(fit) quantile(draws$rho, c(0.025, 0.5, 0.975))
Posterior means (and optional quantiles) of
eta1 = beta0 + a11 w1, the log cell intensity per unit area;
a11w1 = a11 w1, the density latent field;
eta2 = beta1 + a21 w1 + a22 w2, the log expression per cell;
a21w1 = a21 w1, the density-shared part of expression;
a22w2 = a22 w2, the expression-specific part.
denmark_fields(obj, probs = NULL)denmark_fields(obj, probs = NULL)
obj |
A |
probs |
Optional probabilities; for each, the posterior quantiles of
the five fields are added in columns such as |
The full linear predictors are log_lambda1 = log|g| + eta1 and
log_lambda2 = log(Y + 0.05) + eta2.
Data frame with one row per grid cell: grid_index, i, j,
x, y, Y, M, the five fields, log_lambda1 and log_lambda2.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) fields <- denmark_fields(fit) head(fields)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) fields <- denmark_fields(fit) head(fields)
Keeps genes whose variance across cells is at or above the var_quantile
quantile of the gene variances and that are detected (count > 0) in at
least a fraction min_nonzero of the cells. With the defaults this is the
filter used in the DenMark paper: the genes in the lowest 5 percent of
variance are dropped, and at least 5 percent of the cells must have a
non-zero count.
denmark_filter_genes(expr, var_quantile = 0.05, min_nonzero = 0.05)denmark_filter_genes(expr, var_quantile = 0.05, min_nonzero = 0.05)
expr |
Cells x genes matrix or data frame of counts (numeric columns only), with the gene names as column names. |
var_quantile |
Quantile of the gene variances below which genes are dropped. |
min_nonzero |
Minimum fraction of cells with a non-zero count. |
Character vector of the genes kept.
toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) denmark_filter_genes(tumor[, c("geneA", "geneB", "geneC")])toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) denmark_filter_genes(tumor[, c("geneA", "geneB", "geneC")])
Extracts what DenMark needs from a Seurat object with imaging-based spatial
data, such as Xenium, MERSCOPE or CosMx data loaded with LoadXenium(),
LoadVizgen() or LoadNanostring(): the cell centroids, a cell-type label
and the raw counts, one row per cell. Compute the tissue boundary from all
rows with denmark_alpha_shape(), then analyze the cells of one type.
denmark_from_seurat(object, celltype, genes = NULL, assay = NULL, fov = NULL)denmark_from_seurat(object, celltype, genes = NULL, assay = NULL, fov = NULL)
object |
A Seurat object with a field of view holding cell centroids. |
celltype |
Name of the column of the cell metadata that holds the cell types, for example the result of a clustering or an annotation. |
genes |
Genes to include (default: all features of the assay). Each gene becomes a dense column, so select genes for whole-transcriptome panels. |
assay |
Assay holding the raw counts (default: the default assay). |
fov |
Field of view holding the cell centroids (default: the first). |
Data frame with the columns x, y (in the units of the field of
view, micrometers for Xenium), celltype and one count column per gene,
with the cell names as row names.
denmark_annotate() to write the results of denmark_scan() back
into the object.
# a small Seurat object built from the toy data toy <- denmark_toy_data() counts <- Matrix::Matrix(t(as.matrix(toy[, c("geneA", "geneB", "geneC")])), sparse = TRUE) colnames(counts) <- paste0("cell", seq_len(nrow(toy))) obj <- SeuratObject::CreateSeuratObject(counts = counts) obj$celltype <- toy$celltype obj[["fov"]] <- SeuratObject::CreateFOV( SeuratObject::CreateCentroids(data.frame(x = toy$x, y = toy$y, cell = colnames(counts))), type = "centroids", assay = "RNA") cells <- denmark_from_seurat(obj, celltype = "celltype") head(cells)# a small Seurat object built from the toy data toy <- denmark_toy_data() counts <- Matrix::Matrix(t(as.matrix(toy[, c("geneA", "geneB", "geneC")])), sparse = TRUE) colnames(counts) <- paste0("cell", seq_len(nrow(toy))) obj <- SeuratObject::CreateSeuratObject(counts = counts) obj$celltype <- toy$celltype obj[["fov"]] <- SeuratObject::CreateFOV( SeuratObject::CreateCentroids(data.frame(x = toy$x, y = toy$y, cell = colnames(counts))), type = "centroids", assay = "RNA") cells <- denmark_from_seurat(obj, celltype = "celltype") head(cells)
All pairs of basis-function indices, 1..m1 x 1..m2, with the first
index varying fastest, as used by the Stan models.
denmark_hsgp_indices(basis)denmark_hsgp_indices(basis)
basis |
Number of basis functions per axis, or |
Integer matrix with m1 * m2 rows and two columns, without
dimnames.
denmark_hsgp_indices(3) dim(denmark_hsgp_indices(c(10, 8)))denmark_hsgp_indices(3) dim(denmark_hsgp_indices(c(10, 8)))
Returns a function (as expected by cmdstanr) giving valid starting values.
The Cholesky factor of the correlation matrix must be lower triangular
with unit-norm rows: Astar = [[1, 0], [rho0, sqrt(1 - rho0^2)]].
The length-scales start at the prior mean (mrange) and the intercepts at
their empirical values (for beta0, the number of cells per unit of
tissue area), which shortens warm-up.
denmark_init(stan_data, rho0 = 0.3)denmark_init(stan_data, rho0 = 0.3)
stan_data |
Stan data list, as in |
rho0 |
Starting value of rho, in (-1, 1). |
A function without arguments that returns a list of initial values.
toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) prep <- denmark_prepare(tumor, gene = "geneA", grid_size = 10, basis = 10) init <- denmark_init(prep$stan_data) str(init())toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) prep <- denmark_prepare(tumor, gene = "geneA", grid_size = 10, basis = 10) init <- denmark_init(prep$stan_data) str(init())
DenMark.stan, or DenMark-vararea.stan with vararea = TRUE, is looked
up in getOption("denmark.stan_dir") and then in the stan folder of the
installed package. The compiled model is cached for the R session. The
executables of the packaged models are written to
tools::R_user_dir("DenMark", which = "cache") (one subfolder per package
version, because the library may be read-only), so that later sessions
reuse them; any other Stan file is compiled next to itself.
denmark_model( stan_file = NULL, vararea = FALSE, force_recompile = FALSE, quiet = TRUE )denmark_model( stan_file = NULL, vararea = FALSE, force_recompile = FALSE, quiet = TRUE )
stan_file |
Optional path to a Stan file, compiled instead of the packaged model. |
vararea |
Use the variable-area model |
force_recompile |
Recompile even if a compiled model is available. |
quiet |
Suppress the compiler output. |
A cmdstanr CmdStanModel object.
mod <- denmark_model() mod$exe_file()mod <- denmark_model() mod$exe_file()
The grid is centered at the origin, so that the region of interest becomes
[-L1, L1] x [-L2, L2]; the grid centroids are the HSGP input coordinates
and the HSGP domain is boundfactor * c(L1, L2). With a tissue
boundary, the grid cells outside the tissue are removed first and the
region of interest is that of the retained grid.
denmark_prepare( dataset, gene = NULL, grid_size = 10, basis = 25, boundfactor = 1.5, bbox = NULL, L1 = NULL, L2 = NULL, boundary = NULL, min_area_frac = 0 )denmark_prepare( dataset, gene = NULL, grid_size = 10, basis = 25, boundfactor = 1.5, bbox = NULL, L1 = NULL, L2 = NULL, boundary = NULL, min_area_frac = 0 )
dataset |
Data frame with columns |
gene |
Expression column (default: the first non-coordinate column). |
grid_size |
Grid cells per axis, or |
basis |
Number of HSGP basis functions per axis, or |
boundfactor |
HSGP boundary factor |
bbox |
Optional region of interest |
L1, L2
|
Optional half-ranges. Only used to warn if they disagree with the data (the old interface required them; they are now computed). |
boundary |
Optional tissue polygon from |
min_area_frac |
Drop boundary grid cells whose area inside the tissue is below this fraction of a full grid cell (default 0: keep all). |
A list with the elements
stan_data |
Data list for the Stan models: |
grid |
The grid from |
info |
Gene, grid geometry, HSGP settings, number of cells, the
count matrices |
toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) prep <- denmark_prepare(tumor, gene = "geneA", grid_size = 10, basis = 10) str(prep$stan_data[c("n", "mstar", "L", "log_grid_area", "mrange")]) tissue <- denmark_alpha_shape(toy$x, toy$y) prep_b <- denmark_prepare(tumor, gene = "geneA", grid_size = 10, basis = 10, boundary = tissue) prep_b$info[c("n_grid_total", "n_interior", "n_boundary")] summary(exp(prep_b$stan_data$log_grid_area))toy <- denmark_toy_data() tumor <- subset(toy, celltype == "Tumor", select = -celltype) prep <- denmark_prepare(tumor, gene = "geneA", grid_size = 10, basis = 10) str(prep$stan_data[c("n", "mstar", "L", "log_grid_area", "mrange")]) tissue <- denmark_alpha_shape(toy$x, toy$y) prep_b <- denmark_prepare(tumor, gene = "geneA", grid_size = 10, basis = 10, boundary = tissue) prep_b$info[c("n_grid_total", "n_interior", "n_boundary")] summary(exp(prep_b$stan_data$log_grid_area))
cmdstanr keeps posterior draws in CSV files (by default in a temporary
folder that disappears when R closes). denmark_save() reads everything
into memory first, so the saved object can be reloaded and post-processed
in a later session.
denmark_save(obj, file) denmark_load(file)denmark_save(obj, file) denmark_load(file)
obj |
A |
file |
Path of the |
denmark_save() returns file invisibly; denmark_load() returns
the denmark object.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) f <- tempfile(fileext = ".rds") denmark_save(fit, f) fit2 <- denmark_load(f) denmark_summary(fit2)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) f <- tempfile(fileext = ".rds") denmark_save(fit, f) fit2 <- denmark_load(f) denmark_summary(fit2)
A gene is a positively density-correlated gene (PCG) when the
cred_level credible interval of rho lies above 0, a negatively
density-correlated gene (NCG) when it lies below 0, and "not DCG"
otherwise.
denmark_scan( data, genes = NULL, save_dir = NULL, cred_level = 0.95, quiet = TRUE, verbose = TRUE, workers = 1, ... )denmark_scan( data, genes = NULL, save_dir = NULL, cred_level = 0.95, quiet = TRUE, verbose = TRUE, workers = 1, ... )
data |
Data frame with columns |
genes |
Genes to scan (default: all non-coordinate columns). |
save_dir |
Optional folder; one |
cred_level |
Credible level used to call DCGs. |
quiet |
Suppress the sampler output, passed to |
verbose |
Report one message per gene with rho and its credible interval. |
workers |
Number of genes fitted at the same time, each in its own R
session (needs the future.apply package and an installed DenMark). Every
gene keeps its seed, so the results equal those of the sequential scan.
Each fit also runs its chains in parallel ( |
... |
Further arguments passed to |
Data frame with one row per gene, sorted by decreasing posterior
mean of rho: gene, rho_mean, rho_median, rho_lower, rho_upper,
prob_positive, total_counts, max_rhat, min_ess_bulk,
divergences, time_min, error (NA unless the fit failed), class
(PCG, NCG or not DCG) and converged (maximum R-hat below 1.01 and no
divergent transitions). The credible level is stored in
attr(, "cred_level").
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) scan <- denmark_scan(tumor, genes = c("geneA", "geneC"), boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, verbose = FALSE) scan[, c("gene", "rho_mean", "rho_lower", "rho_upper", "class")]toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) scan <- denmark_scan(tumor, genes = c("geneA", "geneC"), boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, verbose = FALSE) scan[, c("gene", "rho_mean", "rho_lower", "rho_upper", "class")]
Cells of the analyzed type follow an inhomogeneous Poisson process with
log lambda1(s) = beta0 + a11 w1(s), and each cell's count for gene k is
Poisson with
log mu_k(s) = beta1 + sigma2 * (rho_k w1(s) + sqrt(1 - rho_k^2) w2k(s)).
denmark_simulate( rho = c(geneA = 0.6, geneB = 0, geneC = -0.6), width = 6, height = 4, n_cells = 5000, ell1 = 1, ell2 = 1, a11 = 1, sigma2 = 0.8, beta1 = 0.5, seed = 1, tissue = c("rectangle", "irregular"), n_other = 0, celltype = "Target" )denmark_simulate( rho = c(geneA = 0.6, geneB = 0, geneC = -0.6), width = 6, height = 4, n_cells = 5000, ell1 = 1, ell2 = 1, a11 = 1, sigma2 = 0.8, beta1 = 0.5, seed = 1, tissue = c("rectangle", "irregular"), n_other = 0, celltype = "Target" )
rho |
Named vector of true correlations, one gene per element. |
width, height
|
Size of the window |
n_cells |
Number of cells of the analyzed type: the thinned point
pattern is subsampled to |
ell1, ell2
|
Length-scales of the density and expression fields. |
a11 |
Standard deviation of the log cell intensity. |
sigma2 |
Standard deviation of the log expression. |
beta1 |
Intercept of the log expression. |
seed |
Random seed, set with |
tissue |
|
n_other |
Number of cells of other types; together with the analyzed cells they define the tissue boundary. |
celltype |
Label of the analyzed cell type (used when |
The fields are smooth, zero-mean, unit-variance Gaussian fields with
approximately Matern-3/2 covariance, drawn by random Fourier features. The
fields are standardized, and each w2k is orthogonalized against w1, on
a regular lattice over the tissue (that is, over space rather than over the
cells), so that the spatial correlation of the two fields equals rho_k,
which is what rho measures in the model.
With tissue = "irregular", the cells lie in a lobed tissue section inside
the window, so that the analysis needs a tissue boundary (see
denmark_alpha_shape()). The cells of other types are drawn from the
candidate locations that the thinning rejected, so they are densest where
the analyzed cells are sparse; their gene counts are Poisson with mean 0.3.
Data frame with the columns x, y, celltype (when
n_other > 0) and one count column per gene; the true values are stored
in attr(, "truth"), including the true tissue outline for
tissue = "irregular".
sim <- denmark_simulate(rho = c(up = 0.5, down = -0.5), n_cells = 500) head(sim) attr(sim, "truth")$rho sec <- denmark_simulate(n_cells = 1000, tissue = "irregular", n_other = 800, celltype = "Tumor") table(sec$celltype)sim <- denmark_simulate(rho = c(up = 0.5, down = -0.5), n_cells = 500) head(sim) attr(sim, "truth")$rho sec <- denmark_simulate(n_cells = 1000, tissue = "irregular", n_other = 800, celltype = "Tumor") table(sec$celltype)
The key parameters are beta0, beta1, ell[1], ell[2], Astar[2,1]
(= rho), sigma[1] and sigma[2].
denmark_summary( obj, variables = NULL, probs = c(0.025, 0.25, 0.5, 0.75, 0.975) )denmark_summary( obj, variables = NULL, probs = c(0.025, 0.25, 0.5, 0.75, 0.975) )
obj |
A |
variables |
Optional override of the variables to summarize. |
probs |
Probabilities of the posterior quantiles to report. |
Data frame with the columns variable, mean, sd, one column
per quantile (q2.5, q25, ...), rhat, ess_bulk, ess_tail and a
descriptive label.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) denmark_summary(fit)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) denmark_summary(fit)
A simulated tissue section: 3,449 cells of the analyzed type ("Tumor")
and 4,000 cells of other types ("Other") in an irregular, lobed tissue.
The Tumor cells carry three genes whose density-expression correlations
are known: geneA (rho = 0.6), geneB (rho = 0) and geneC (rho = -0.6).
The other cells only help delineate the tissue: compute the boundary with
denmark_alpha_shape() from all cells, then analyze the Tumor cells. The
examples, the tests and the vignette use these data, so every result can
be checked against the truth.
denmark_toy_data()denmark_toy_data()
The file inst/extdata/denmark_toy.csv.gz was generated by
data-raw/make_toy_data.R with denmark_simulate(), using
rho = c(geneA = 0.6, geneB = 0, geneC = -0.6), width = 6, height = 4,
n_cells = 5000, seed = 1, tissue = "irregular", n_other = 4000,
celltype = "Tumor" and the defaults for the other arguments
(ell1 = 1, ell2 = 1, a11 = 1, sigma2 = 0.8, beta1 = 0.5); the
coordinates were rounded to six decimals. The Python package reads a byte
copy of the same file with denmark.datasets.load_toy().
Data frame with one row per cell and the columns x and y (cell
locations in the window [0, 6] x [0, 4]), celltype ("Tumor" or
"Other") and geneA, geneB and geneC (counts).
toy <- denmark_toy_data() head(toy) table(toy$celltype) tumor <- subset(toy, celltype == "Tumor", select = -celltype)toy <- denmark_toy_data() head(toy) table(toy$celltype) tumor <- subset(toy, celltype == "Tumor", select = -celltype)
Keeps scripts written for the original interface running. L1 and L2
are no longer needed (they are computed from the data) and are only
checked.
DenMarkmain( grid_size, dataset, basis, boundfactor, L1 = NULL, L2 = NULL, denmark_version = "M2", ... )DenMarkmain( grid_size, dataset, basis, boundfactor, L1 = NULL, L2 = NULL, denmark_version = "M2", ... )
grid_size |
Grid cells per axis, or |
dataset |
Data frame with columns |
basis |
HSGP basis functions per axis, or |
boundfactor |
HSGP boundary factor (>= 1). |
L1, L2
|
Optional half-ranges, only checked against the data. |
denmark_version |
|
... |
Further arguments passed to |
An object of class denmark, see DenMark().
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- toy[toy$celltype == "Tumor", c("x", "y", "geneA")] fit <- DenMarkmain(grid_size = 10, dataset = tumor, basis = 10, boundfactor = 1.5, boundary = tissue, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) fit$rhattoy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- toy[toy$celltype == "Tumor", c("x", "y", "geneA")] fit <- DenMarkmain(grid_size = 10, dataset = tumor, basis = 10, boundfactor = 1.5, boundary = tissue, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) fit$rhat
Grid centroids, grid area and number of grids
grid_pts_centroid(grid_size, dataset, bbox = NULL)grid_pts_centroid(grid_size, dataset, bbox = NULL)
grid_size |
Number of grid cells per axis, or |
dataset |
Data frame with the columns |
bbox |
Optional bounding box |
list(grid_points, grid_area, N, step, bbox); the first three
elements keep the order of the original function. grid_points are the
true centroids of the grid cells (x varying fastest).
toy <- denmark_toy_data() geom <- grid_pts_centroid(grid_size = 10, dataset = toy) c(grid_area = geom$grid_area, n_grids = geom$N)toy <- denmark_toy_data() geom <- grid_pts_centroid(grid_size = 10, dataset = toy) c(grid_area = geom$grid_area, n_grids = geom$N)
Matern covariance with smoothness 3/2, in the same parameterization as the
Stan models: C(d) = sigma^2 (1 + sqrt(3) d / ell) exp(-sqrt(3) d / ell).
The argument name rho (the length-scale ell) is kept for backward
compatibility.
matern32(d, sigma = 1, rho)matern32(d, sigma = 1, rho)
d |
Distances. |
sigma |
Marginal standard deviation. |
rho |
Length-scale |
Numeric vector of covariances, one per element of d.
matern32(c(0, 0.5, 1, 2), rho = 1)matern32(c(0, 0.5, 1, 2), rho = 1)
Left: all cells (gray), the cells of the analyzed type (colored) and the
alpha-shape tissue boundary. Right (when grid is given): the grid cells
colored by type (interior, boundary, exterior) with the boundary outline.
plot_denmark_boundary( cells, boundary, grid = NULL, highlight = NULL, xlab = "x", ylab = "y", point_size = 0.15 )plot_denmark_boundary( cells, boundary, grid = NULL, highlight = NULL, xlab = "x", ylab = "y", point_size = 0.15 )
cells |
Data frame with the columns |
boundary |
Polygon from |
grid |
Optional output of |
highlight |
Optional logical vector, one element per row of |
xlab, ylab
|
Axis labels. |
point_size |
Size of the cell points. |
A ggplot object; with grid, the two panels combined with
patchwork, or a list of two ggplot objects when patchwork is not
installed.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) g <- buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA", boundary = tissue) plot_denmark_boundary(toy, tissue, grid = g, highlight = toy$celltype == "Tumor")toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) g <- buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA", boundary = tissue) plot_denmark_boundary(toy, tissue, grid = g, highlight = toy$celltype == "Tumor")
Posterior mean and band of the spatial correlation of the density and expression fields, with the threshold that defines the practical range as a dashed line.
plot_denmark_decay(obj, decay = NULL, xlab = "Distance")plot_denmark_decay(obj, decay = NULL, xlab = "Distance")
obj |
A |
decay |
Optional output of |
xlab |
Label of the distance axis. |
A ggplot object.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) plot_denmark_decay(fit)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) plot_denmark_decay(fit)
Row 1 (cell density): beta0 + a11 w1 and a11 w1, on a shared color
scale. Row 2 (expression): beta1 + a21 w1 + a22 w2, a21 w1 and
a22 w2, on a shared color scale.
plot_denmark_fields(obj, fields = NULL, xlab = "x", ylab = "y")plot_denmark_fields(obj, fields = NULL, xlab = "x", ylab = "y")
obj |
A |
fields |
Optional output of |
xlab, ylab
|
Axis labels. |
A patchwork figure, or a list of five ggplot objects when patchwork is not installed.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) plot_denmark_fields(fit)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) fit <- DenMark(tumor, gene = "geneA", boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, quiet = TRUE) plot_denmark_fields(fit)
Tile maps of the cell count and the summed expression of each grid cell. With a tissue boundary, only the grid cells inside the tissue are drawn, together with the tissue outline.
plot_denmark_grid(obj, xlab = "x", ylab = "y")plot_denmark_grid(obj, xlab = "x", ylab = "y")
obj |
A |
xlab, ylab
|
Axis labels. |
The two panels combined with patchwork, or a list of two ggplot objects when patchwork is not installed.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) plot_denmark_grid(buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA", boundary = tissue))toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) plot_denmark_grid(buildgridpp(grid_size = 10, dataset = tumor, gene = "geneA", boundary = tissue))
Posterior mean and credible interval of rho for each gene, colored by the DCG call.
plot_denmark_scan(scan, top = NULL)plot_denmark_scan(scan, top = NULL)
scan |
Data frame returned by |
top |
Optional number of genes to show, those with the largest absolute posterior mean of rho. |
A ggplot object.
toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) scan <- denmark_scan(tumor, genes = c("geneA", "geneC"), boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, verbose = FALSE) plot_denmark_scan(scan)toy <- denmark_toy_data() tissue <- denmark_alpha_shape(toy$x, toy$y) tumor <- subset(toy, celltype == "Tumor", select = -celltype) scan <- denmark_scan(tumor, genes = c("geneA", "geneC"), boundary = tissue, grid_size = 10, basis = 10, chains = 2, iter_warmup = 300, iter_sampling = 300, seed = 2024, verbose = FALSE) plot_denmark_scan(scan)
Shorthands for the 2.5th, 25th, 50th, 75th and 97.5th percentiles of a vector of posterior draws, kept for compatibility with earlier scripts.
q2.5(x) q25(x) q50(x) q75(x) q97.5(x)q2.5(x) q25(x) q50(x) q75(x) q97.5(x)
x |
Numeric vector, for example the posterior draws of one parameter. |
A single unnamed number: the sample quantile of x from
stats::quantile() with its default method.
x <- 0:100 c(q2.5(x), q25(x), q50(x), q75(x), q97.5(x))x <- 0:100 c(q2.5(x), q25(x), q50(x), q75(x), q97.5(x))