Skip to content

Draft: add contour-based ring density and smooth signed-distance density for SpatialData#1163

Draft
hutaobo wants to merge 2 commits intoscverse:mainfrom
hutaobo:codex/contour-density-draft
Draft

Draft: add contour-based ring density and smooth signed-distance density for SpatialData#1163
hutaobo wants to merge 2 commits intoscverse:mainfrom
hutaobo:codex/contour-density-draft

Conversation

@hutaobo
Copy link
Copy Markdown

@hutaobo hutaobo commented Apr 16, 2026

Description

This draft PR proposes contour-aware density quantification around polygon annotations in SpatialData.

It adds two related tools:

  1. sq.tl.ring_density(...)
    Computes inward/outward ring-based density profiles around polygon contours for cells or transcripts, using signed-distance semantics relative to the contour boundary.

  2. sq.tl.smooth_density_by_distance(...)
    Computes a continuous signed-distance density profile for transcripts using Gaussian kernel smoothing on signed distance, while keeping an area-normalized density interpretation.

The main motivation is to support workflows such as tumor boundary, lumen edge, or compartment-interface analyses where users want density profiles around annotated contours rather than only nearest-distance values.

This draft intentionally includes both a discrete ring-based summary and a continuous smooth alternative so the API direction can be reviewed together. I realize that issue #1160 scoped smoothing as out of scope for a minimal v1, so I would especially welcome feedback on whether the smooth method should be split into a follow-up PR or deferred.

How has this been tested?

Tested locally with:

  • focused synthetic tests for ring_density
  • focused synthetic tests for smooth_density_by_distance
  • combined local test run:
    • pytest -q tests/tools/test_ring_density.py tests/tools/test_smooth_density_by_distance.py

The smooth method tests cover:

  • output structure and metadata
  • .uns storage when copy=False
  • boundary-enriched smooth behavior
  • consistency across Dask partitions
  • local shell-area geometry normalization
  • reflection-based boundary correction

I also ran a real-data smoke test on a local SpatialData object with:

  • protein_cluster_contours
  • assigned_structure == "Structure 4"
  • transcript feature VIM

This produced a continuous signed-distance output table with 23 contours and 33 distance grid points.

Closes

Closes #1160

@hutaobo
Copy link
Copy Markdown
Author

hutaobo commented Apr 16, 2026

Thanks for taking a look. I would especially appreciate feedback on three design questions:\n\n1. Does belong in as proposed?\n2. Should stay in this same PR for joint API review, or would you prefer it split or deferred?\n3. Does it make sense to eventually factor the signed-distance computation into a lower-level helper shared by both tools?\n\nI also generated local example figures for the ring-based and smooth profiles and can add them in a follow-up comment if that would be helpful.

@hutaobo
Copy link
Copy Markdown
Author

hutaobo commented Apr 16, 2026

Thanks for taking a look. I would especially appreciate feedback on three design questions:

  1. Does sq.tl.ring_density(...) belong in tl as proposed?
  2. Should sq.tl.smooth_density_by_distance(...) stay in this same PR for joint API review, or would you prefer it split or deferred?
  3. Does it make sense to eventually factor the signed-distance computation into a lower-level helper shared by both tools?

I also generated local example figures for the ring-based and smooth profiles and can add them in a follow-up comment if that would be helpful.

@timtreis
Copy link
Copy Markdown
Member

Hey @hutaobo, could you demonstrate on real data which biological insights one is getting from that?

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 77.57732% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.77%. Comparing base (373228d) to head (cf1589a).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/squidpy/tl/_ring_density.py 76.44% 26 Missing and 27 partials ⚠️
src/squidpy/tl/_smooth_density_by_distance.py 79.14% 17 Missing and 17 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1163      +/-   ##
==========================================
+ Coverage   73.56%   73.77%   +0.21%     
==========================================
  Files          44       46       +2     
  Lines        6929     7317     +388     
  Branches     1174     1243      +69     
==========================================
+ Hits         5097     5398     +301     
- Misses       1347     1390      +43     
- Partials      485      529      +44     
Files with missing lines Coverage Δ
src/squidpy/tl/_smooth_density_by_distance.py 79.14% <79.14%> (ø)
src/squidpy/tl/_ring_density.py 76.44% <76.44%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: contour-based inward/outward ring density for cells and transcripts around polygon annotations

2 participants