From 38550ffbec697e589607a92683b770ba751d997d Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Tue, 28 Jul 2026 14:31:32 +0200 Subject: [PATCH 1/2] normalise the simulated data the same way as the real data `downstream` built the simulated `logcounts` with `log1p(counts)`, but read the real dataset's stored `logcounts` layer for the other side of the comparison. That layer is `log2(counts / sizeFactor + 1)` -- verified exactly against the shipped dataset -- so the two sides differed by a whole transform and Moran's I partly measured that rather than the data. Both sides now go through a shared `compute_logcounts()`, which reproduces the stored layer to 9e-16, so the real side is unchanged and only the simulated side moves. On the positive control, where the simulated data is the real data, crosscor_cosine and crosscor_mantel go from 0.95 to exactly 1. --- src/helpers/utils.R | 25 +++++++++++++++++++++++++ src/metrics/downstream/script.R | 6 +++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/helpers/utils.R b/src/helpers/utils.R index b9ec2703..c4b46ea7 100644 --- a/src/helpers/utils.R +++ b/src/helpers/utils.R @@ -1,3 +1,28 @@ +# log-normalisation +# +# Both the real and the simulated dataset must be normalised the exact same way +# before they are compared, otherwise the metric picks up the difference in +# normalisation rather than the difference in the data. Do not read the +# `logcounts` layer directly: on the real dataset it was produced by the dataset +# loader, on a simulated dataset it may not exist at all. +compute_logcounts <- function(adata) { + requireNamespace("scater", quietly = TRUE) + requireNamespace("SingleCellExperiment", quietly = TRUE) + requireNamespace("SummarizedExperiment", quietly = TRUE) + + # genes x spots, as expected by SingleCellExperiment + counts <- Matrix::t(adata$layers[["counts"]]) + + sce <- SingleCellExperiment::SingleCellExperiment(list(counts = counts)) + sce <- scater::logNormCounts(sce) + + logcounts <- SummarizedExperiment::assay(sce, "logcounts") + dimnames(logcounts) <- dimnames(counts) + + # back to spots x genes, matching the anndata layout + Matrix::t(logcounts) +} + # spatial autocorrelation generate_moransI <- function(adata) { requireNamespace("spots", quietly = TRUE) diff --git a/src/metrics/downstream/script.R b/src/metrics/downstream/script.R index 51980152..a8d59f43 100644 --- a/src/metrics/downstream/script.R +++ b/src/metrics/downstream/script.R @@ -34,9 +34,9 @@ ctdeconvolute_rmse <- generate_rmse(real_ct_prop, sim_ct_prop) ctdeconvolute_jsd <- generate_jds(real_ct_prop, sim_ct_prop) cat("spatial autocorrelation evaluation\n") -counts <- input_simulated_sp$layers[["counts"]] -logcounts <- log1p(counts) -input_simulated_sp$layers[["logcounts"]] <- logcounts +# normalise both datasets the same way, so that Moran's I is comparable +input_real_sp$layers[["logcounts"]] <- compute_logcounts(input_real_sp) +input_simulated_sp$layers[["logcounts"]] <- compute_logcounts(input_simulated_sp) real_moransI <- generate_moransI(input_real_sp) # real_moransI <- input_real_sp$varm$spatial_autocorrelation sim_moransI <- generate_moransI(input_simulated_sp) From db076bb041d54325f7e2129834d5f04daa2eba71 Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Tue, 28 Jul 2026 14:31:32 +0200 Subject: [PATCH 2/2] update changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3468069..be1c40dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # task_spatial_simulators dev +Bug fixes: + - `downstream`: normalise the simulated dataset the same way as the real one, + through a new `compute_logcounts()` helper. The simulated side used + `log1p(counts)` while the real side used the stored, size-factor normalised + `logcounts`. On the positive control, `crosscor_cosine` and + `crosscor_mantel` now come out at exactly 1. + +# task_spatial_simulators dev + Bug fixes: - `run_benchmark`: raise `uns_length_cutoff` from 15 to 50, so that `extract_uns_metadata` no longer drops the `metric_ids` of components that