scanpy icon indicating copy to clipboard operation
scanpy copied to clipboard

Custom neighbor transformers with varying neighbor count and respect `knn`

Open maltekuehl opened this issue 5 months ago • 1 comments

Allows neighbor counts < k in sc.pp.neighbors by adding masking to _ind_dist_slow, enabling adaptive neighbor transformers. Draft for now, as I could not run tests locally or on CI (following the exact installation instructions and not touching the GitHub actions, so it seems like the workflow is broken). I would also appreciate help in making the tests more idiomatic to scanpy and getting some feedback on if this could break any other part that might rely on connectivities having a certain shape.

To make it work, I also had to take on #3014 (more discussion in that issue). Adding a binary connectivity method like in this PR would be backwards compatible and unlock many different use cases. In my opinion, the current implementation is however quite confusing for end users and some documentation clarity, simplification and warning about unexpected behavior when using knn with method="umap" would be helpful. Since some of this could result in breaking changes, this PR can hopefully also serve as a place for discussion on how to implement this.

The second part has also been discussed in:

  • #2587
  • #1984

#3021 might also be related to the overall complex structure with multiple distance calculations and changes to the connectivities in neighbors, though I have not been able to confirm it yet.

Use cases of these changes:

  • Full comparability with use of transformers outside of scanpy

  • SNN transformers

  • Distance adaptive algorithms

  • Less duplication when implemeting methods like bbknn, etc.

  • [X] Closes #3806 and closes #3014

  • [X] Tests included: Needs additional tests to verify n_neighbors in distances and connectivities for different methods and different n_obs (this test might have prevented #3809)

maltekuehl avatar Sep 19 '25 15:09 maltekuehl

Codecov Report

:x: Patch coverage is 84.37500% with 5 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 76.85%. Comparing base (f75ac11) to head (fe1ba5e). :white_check_mark: All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/scanpy/neighbors/_common.py 80.00% 5 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3807      +/-   ##
==========================================
+ Coverage   76.72%   76.85%   +0.13%     
==========================================
  Files         116      116              
  Lines       12398    12413      +15     
==========================================
+ Hits         9512     9540      +28     
+ Misses       2886     2873      -13     
Flag Coverage Δ
hatch-test.pre 76.85% <84.37%> (+0.13%) :arrow_up:

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/scanpy/neighbors/__init__.py 80.26% <100.00%> (+0.20%) :arrow_up:
src/scanpy/neighbors/_types.py 100.00% <100.00%> (ø)
src/scanpy/neighbors/_common.py 89.70% <80.00%> (+24.79%) :arrow_up:

codecov[bot] avatar Dec 08 '25 20:12 codecov[bot]