First release as installable packages. The R functions and the Python module that were sourced from DenMark/ are now the R package DenMark, at the repository root, and the Python package denmark, in python/. The R functions are those of Mingchi Xu's revision, which adds the tissue boundary (see below), and the Python package carries the same revision. Apart from how the Stan files are found and where compiled models are cached, the functions and the Stan model are carried over unchanged, with one exception that follows the revised manuscript: only the DenMark model is kept. The simplified model with independent fields (formerly model = "M1"), the model argument and WAIC are removed, with denmark_compare() and fit_simple in R and compare_models(), waic() and fit_simple in Python, and the Stan files drop the M2 label. Fits of the model give the same draws as model = "M2" did for the same Stan data.
install.packages("DenMark", repos = c("https://stagill.r-universe.dev", "https://stan-dev.r-universe.dev", "https://cloud.r-project.org"))) and the Python package from GitHub (pip install "git+https://github.com/StaGill/DenMark#subdirectory=python"); both build the same Stan data from the same input (with a tissue boundary, the grid areas agree up to the last bit, since sf and shapely compute polygon areas with different formulas).inst/stan/, with byte copies in the Python package: the DenMark model (DenMark.stan), its variable-area version, which the packages fit with a tissue boundary (DenMark-vararea.stan), and its total-UMI version used in the revision analyses (DenMark-vararea-umi.stan).denmark_toy_data() in R and denmark.datasets.load_toy() in Python.CodeInPaper/: the scripts behind the analyses of the revised manuscript, with configurable data and output paths.Mingchi Xu's revision of the analysis functions (DenMark/DenMark-functions.R, 2026-10-24) restricts inference to the tissue, as the revised manuscript does. The Python package has the same functions, ported from his R code.
denmark_alpha_shape() (Python: alpha_shape()) delineates the tissue as an alpha shape of the cell locations: the union of the Delaunay triangles whose circumradius is below alpha, by default three times the median Delaunay edge length. It is computed from all cells of the section, because the region where the analyzed cell type could have been observed is the whole tissue.boundary = in buildgridpp(), denmark_prepare(), DenMark() and denmark_scan() (Python: buildgridpp(), prepare_data(), DenMark(), scan_genes()) classifies the grid cells as interior, boundary or exterior and gives each its area inside the tissue; from denmark_prepare() on, the exterior grid cells are dropped. log_grid_area then becomes a vector, and DenMark() fits DenMark-vararea.stan. min_area_frac optionally drops boundary grid cells with less than that fraction of a full grid cell inside the tissue.boundary, the bounding box is gridded with a common grid area, as before: the Stan data differ from those of the earlier functions only by floating-point rounding of coords and L (at most 9e-16 on the toy data), and the starting values agree up to the last bit unless the grids hold fewer than 0.1 cells on average.plot_denmark_boundary() (Python: plot_boundary()) shows the cells, the boundary and the classification of the grid cells; plot_denmark_grid() and plot_denmark_fields() draw the tissue outline and leave out the exterior grid cells.denmark_simulate() (Python: simulate()) gains tissue = "irregular", which places the cells in a lobed tissue section, and n_other and celltype, which add cells of other types drawn from the locations that the thinning rejected, so that they are densest where the analyzed cells are sparse. The fields are now standardized and orthogonalized on a lattice over the tissue (over space rather than over the cells), so a seed no longer reproduces the data of the earlier functions.Tutorial_DenMark.Rmd, with a celltype column, 3,449 Tumor cells that carry the three genes and 4,000 Other cells that only delineate the tissue. The examples compute the boundary from all cells and analyze the Tumor cells.buildgridpp() reports the cells that lie outside the extent of the boundary.denmark_init() (Python: make_init()) starts beta0 at the log of the number of cells per unit of tissue area.boundary (pip install "denmark[boundary] @ git+https://github.com/StaGill/DenMark#subdirectory=python") or with pip install shapely.denmark_from_seurat() reads the cell centroids, a cell-type column and the raw counts from a Seurat object with imaging-based data (Xenium, MERSCOPE, CosMx), and denmark_annotate() writes the scan results into its gene metadata; SeuratObject is a new suggested package. In Python, from_anndata() reads an AnnData object (scanpy, squidpy, or a SpatialData table from spatialdata-io) and annotate() writes the results into adata.var.denmark_scan(workers = ) and scan_genes(n_jobs = ) fit several genes at a time (R: separate sessions through future.apply, a new suggested package; Python: threads, each driving its own CmdStan processes). Every gene keeps its seed, so the results equal those of the sequential scan.inst/slurm/ holds a template that fits one gene per SLURM array task, can be resubmitted after an interruption, and collects the results with denmark_scan().