spatial-aim 1.3.0__tar.gz → 1.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/PKG-INFO +28 -25
  2. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/README.md +26 -23
  3. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/environment.yml +1 -1
  4. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/pyproject.toml +1 -1
  5. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/PKG-INFO +28 -25
  6. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/SOURCES.txt +7 -2
  7. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/requires.txt +1 -1
  8. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/scm_file_list.json +7 -2
  9. spatial_aim-1.4.0/src/spatial_aim.egg-info/scm_version.json +8 -0
  10. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/adata_schema.py +6 -3
  11. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/coherence.py +4 -44
  12. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/compare_ks/__init__.py +2 -2
  13. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/compare_ks/collect.py +37 -22
  14. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/compare_ks/criteria.py +5 -6
  15. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/compare_ks/scoring.py +12 -33
  16. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/reconstruction.py +25 -46
  17. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/run.py +3 -3
  18. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/shape_consistency.py +4 -3
  19. spatial_aim-1.4.0/src/spatialaim/analysis/topology.py +65 -0
  20. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/cli.py +25 -32
  21. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/clustering.py +33 -26
  22. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/config.py +7 -5
  23. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_03_mouse_liver.py +3 -13
  24. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_04_mouse_kidney.py +27 -42
  25. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_05_mouse_embryo.py +20 -45
  26. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_06_mouse_retina.py +5 -43
  27. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_07_mouse_mpfc.py +2 -0
  28. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_08_mouse_visual_cortex.py +36 -48
  29. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/__main__.py +6 -0
  30. spatial_aim-1.4.0/src/spatialaim/gui/app.py +189 -0
  31. spatial_aim-1.4.0/src/spatialaim/gui/compute.py +175 -0
  32. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/data_access.py +138 -36
  33. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/loaders.py +8 -4
  34. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/__init__.py +1 -1
  35. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/cell_type.py +17 -49
  36. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/ksweep.py +9 -22
  37. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/markers.py +15 -7
  38. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/report.py +25 -12
  39. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/scatter.py +54 -33
  40. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/scaffold.py +30 -28
  41. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/sidebar.py +159 -82
  42. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/state_names.py +74 -14
  43. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/texts.py +0 -16
  44. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/cards.py +0 -20
  45. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/cell_type_modal.py +8 -17
  46. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/compare_tab.py +12 -12
  47. spatial_aim-1.4.0/src/spatialaim/gui/views/controls.py +80 -0
  48. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/mapper_tab.py +104 -20
  49. spatial_aim-1.4.0/src/spatialaim/gui/views/overview.py +116 -0
  50. spatial_aim-1.4.0/src/spatialaim/gui/views/pages.py +258 -0
  51. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/report.py +2 -52
  52. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/io.py +19 -7
  53. spatial_aim-1.4.0/src/spatialaim/metrics/topology.py +65 -0
  54. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/sweep.py +112 -57
  55. spatial_aim-1.4.0/src/spatialaim/utils.py +37 -0
  56. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/tests/test_cli.py +30 -8
  57. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/tests/test_gui_imports.py +2 -0
  58. spatial_aim-1.4.0/tests/test_gui_overview.py +53 -0
  59. spatial_aim-1.4.0/tests/test_sweep_sample.py +197 -0
  60. spatial_aim-1.4.0/tests/test_topology.py +67 -0
  61. spatial_aim-1.4.0/tests/test_utils.py +25 -0
  62. spatial_aim-1.3.0/src/spatial_aim.egg-info/scm_version.json +0 -8
  63. spatial_aim-1.3.0/src/spatialaim/analysis/spatial_organization.py +0 -122
  64. spatial_aim-1.3.0/src/spatialaim/gui/app.py +0 -228
  65. spatial_aim-1.3.0/src/spatialaim/gui/compute.py +0 -112
  66. spatial_aim-1.3.0/src/spatialaim/gui/views/controls.py +0 -46
  67. spatial_aim-1.3.0/src/spatialaim/metrics/topology.py +0 -49
  68. spatial_aim-1.3.0/src/spatialaim/utils.py +0 -18
  69. spatial_aim-1.3.0/tests/test_sweep_sample.py +0 -126
  70. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/.github/workflows/ci.yml +0 -0
  71. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/.github/workflows/release.yml +0 -0
  72. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/.gitignore +0 -0
  73. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/.pre-commit-config.yaml +0 -0
  74. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/LICENSE +0 -0
  75. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/docs/aim_gui.gif +0 -0
  76. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/docs/figure_2_1.png +0 -0
  77. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/renovate.json +0 -0
  78. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/sample_dataset/ST/index.csv +0 -0
  79. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/sample_dataset/ST/sample_st.h5ad +0 -0
  80. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/sample_dataset/pairs.csv +0 -0
  81. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/sample_dataset/scRNA/index.csv +0 -0
  82. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/sample_dataset/scRNA/sample_sc.h5ad +0 -0
  83. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/setup.cfg +0 -0
  84. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/dependency_links.txt +0 -0
  85. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/entry_points.txt +0 -0
  86. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatial_aim.egg-info/top_level.txt +0 -0
  87. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/__init__.py +0 -0
  88. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/__main__.py +0 -0
  89. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/aggregation.py +0 -0
  90. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/__init__.py +0 -0
  91. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/compare_ks/run.py +0 -0
  92. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/compare_ks/selection.py +0 -0
  93. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/analysis/loading.py +0 -0
  94. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/__init__.py +0 -0
  95. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/README.md +0 -0
  96. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/__init__.py +0 -0
  97. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/_common.py +0 -0
  98. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/_segmentation.py +0 -0
  99. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_01_mouse_ssp.py +0 -0
  100. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_02_colorectal_cancer.py +0 -0
  101. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_09_human_breast_cancer.py +0 -0
  102. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/curate_10_human_mtg.py +0 -0
  103. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/dump_retina_counts.R +0 -0
  104. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/curate_datasets/environment_retina_qs.yml +0 -0
  105. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/__init__.py +0 -0
  106. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/common.py +0 -0
  107. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/runner.py +0 -0
  108. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/validate_pair.py +0 -0
  109. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/validate_sc.py +0 -0
  110. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/validate_segmentation.py +0 -0
  111. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/data/validate/validate_st.py +0 -0
  112. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/__init__.py +0 -0
  113. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/common.py +0 -0
  114. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/headline.py +0 -0
  115. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/render/shape.py +0 -0
  116. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/validation.py +0 -0
  117. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/__init__.py +0 -0
  118. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/choose_k.py +0 -0
  119. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/views/reference_tab.py +0 -0
  120. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/widgets/__init__.py +0 -0
  121. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/widgets/headline_plot.py +0 -0
  122. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/widgets/linked_plot.py +0 -0
  123. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/widgets/plotly_fit.py +0 -0
  124. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/gui/widgets/slider.py +0 -0
  125. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/mapping/__init__.py +0 -0
  126. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/mapping/base.py +0 -0
  127. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/mapping/confidence.py +0 -0
  128. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/mapping/nearest_centroid.py +0 -0
  129. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/mapping/reference.py +0 -0
  130. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/mapping/wann.py +0 -0
  131. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/metrics/__init__.py +0 -0
  132. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/metrics/cossim.py +0 -0
  133. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/metrics/modularity.py +0 -0
  134. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/metrics/shape_consistency.py +0 -0
  135. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/__init__.py +0 -0
  136. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/environment_dot.yml +0 -0
  137. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/environment_tacco.yml +0 -0
  138. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/environment_tangram.yml +0 -0
  139. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/registry.py +0 -0
  140. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/run_dot.R +0 -0
  141. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/run_dot.py +0 -0
  142. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/run_tacco.py +0 -0
  143. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/reference_aligners/run_tangram.py +0 -0
  144. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/src/spatialaim/tree.py +0 -0
  145. {spatial_aim-1.3.0 → spatial_aim-1.4.0}/tests/test_registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatial-aim
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: SpatialAIM — annotation-independent mapping of scRNA-seq references onto high-resolution spatial transcriptomics
5
5
  Author-email: Dominik Pysch <dominik.pysch@fau.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -705,7 +705,7 @@ Requires-Dist: umap-learn>=0.5
705
705
  Requires-Dist: tifffile>=2024.8
706
706
  Requires-Dist: zarr>=2.16
707
707
  Requires-Dist: scikit-image>=0.22
708
- Requires-Dist: streamlit>=1.40
708
+ Requires-Dist: streamlit>=1.61
709
709
  Requires-Dist: plotly>=5.20
710
710
  Requires-Dist: kaleido>=0.2
711
711
  Provides-Extra: data
@@ -747,8 +747,8 @@ The spot→state step is **modular**. In principle, any scRNA to ST alignment to
747
747
  We implement two baseline mappers (`nearest_centroid`, `wann`) and three reference mappers (`tangram`, `tacco`, `dot`).
748
748
  You are invited to add and try out your mapper of choice!
749
749
 
750
- If you want to replace the Leiden overclustering from Step 1 with your own annotations,
751
- `--start_from_annotation` takes an existing annotation's `L` cell types as the start clusters instead.
750
+ If you also want to start Step 1 from your own annotations,
751
+ `--annotation` runs the sweep a second time (once per annotation) with an existing annotation's `L` cell types as the start clusters.
752
752
 
753
753
  ![aim_figure](./docs/figure_2_1.png)
754
754
 
@@ -864,8 +864,10 @@ spatialaim gui [--server_port 8501]
864
864
 
865
865
  Open the printed URL (default http://localhost:8501), set the inputs, pick one or multiple mapper(s), and click **Run**.
866
866
  As soon as both `.h5ad` paths are set, the GUI runs the same checks as [`spatialaim validate`](#validate-a-dataset-pair-spatialaim-validate) on that pair: a clean pair is confirmed with a tick in the sidebar, any error or warning is shown in a dialog you can close to continue anyway.
867
- The GUI writes each mapper's sweep to `<output_dir>/<mapper>/`. The results using each mapper are visualized in different tabs. When having run multiple mappers, also a "Compare" tab appears.
868
- An optional **Segmentation path** (the ST slice's cell segmentation mask, see the sidebar's *Info*) is validated with the pair and passed to the runs; their mapper tabs then get a "Shape features" card (shape consistency and the cells' area / eccentricity / solidity per cell type), and the cell-type modal a "Shape features" view. Without it the GUI is unchanged.
867
+ The GUI always runs from the Leiden over-clustering; pick annotations under *Also start from annotations* to additionally start from them (the same as `--annotation`).
868
+ It writes the same layout as the CLI, `<output_dir>/<start>/<mapper>/`. The results are pages in a top navigation: **Overview** (a table of every starting point × mapper at its best overall K, sorted by the overall score; a selected row opens it in Explore), **Explore** (pick starting clusters, method and K in one control bar; switching start or method lands on that combination's best K), **Compare** (several methods of one starting point side by side, once two have finished) and **Single-cell reference**.
869
+ Once an output folder holds results, its linkage is fixed. Mappers and annotations can still be added: the next *Run* computes every missing starting point × mapper combination. Output folders written by earlier versions (`<output_dir>/<mapper>/`) can still be opened and viewed.
870
+ An optional **Segmentation path** (the ST slice's cell segmentation mask, see the sidebar's *Info*) is validated with the pair and passed to the runs; their Explore views then get a "Shape features" card (shape consistency and the cells' area / eccentricity / solidity per cell type), and the cell-type modal a "Shape features" view. Without it the GUI is unchanged.
869
871
 
870
872
  ### Run via CLI (`spatialaim run`)
871
873
 
@@ -881,10 +883,10 @@ The linkage of the agglomeration tree over the start clusters is chosen with `--
881
883
  - **`average`** (default): UPGMA — average pairwise distance; no size term, tends to peel small tight groups off a growing dominant state.
882
884
  - **`ward`**: Ward's criterion (R's `ward.D`); carries a size term and tends to produce balanced states.
883
885
 
884
- The **start clusters** the tree is built over come from the Leiden over-clustering by default.
885
- Pass `--start_from_annotation <obs_column>` to use a pre-existing annotation instead: its cell types become the start clusters, no over-clustering is computed at all,
886
- and `K` sweeps from the number of annotated types down (cells with no label are dropped).
887
- Give such runs their own `--output_dir`: a run root is named after the mapper alone, so the two modes would otherwise overwrite each other.
886
+ The **start clusters** the tree is built over come from the Leiden over-clustering.
887
+ Pass `--annotation <obs_column> [<obs_column> ...]` to additionally start from pre-existing annotations: the sweep then runs once from the Leiden clusters
888
+ and once per annotation, whose cell types become the start clusters (`K` sweeps from the number of annotated types down).
889
+ Every annotation must label every cell; a missing column or an unlabelled cell is an error (in batch mode, that pair fails).
888
890
 
889
891
  **Single pair**
890
892
 
@@ -895,7 +897,7 @@ spatialaim run --scdata path/to/sc.h5ad \
895
897
  [--segmentation path/to/mask.tif] \
896
898
  [--mapping nearest_centroid|wann|tangram|tacco|dot] \
897
899
  [--linkage_method average|ward] \
898
- [--start_from_annotation <obs_column>] \
900
+ [--annotation <obs_column> ...] \
899
901
  [--leiden_resolution 3.0] \
900
902
  [--k_min 1] [--k_max <L>] [--k_step 1] \
901
903
  [--logging verbose]
@@ -912,7 +914,7 @@ spatialaim run --pairs_csv path/to/pairs.csv \
912
914
  --output_dir path/to/out_dir \
913
915
  [--mapping nearest_centroid|wann|tangram|tacco|dot] \
914
916
  [--linkage_method average|ward] \
915
- [--start_from_annotation <obs_column>] \
917
+ [--annotation <obs_column> ...] \
916
918
  [--leiden_resolution 3.0] \
917
919
  [--k_min 1] [--k_max <L>] [--k_step 1] \
918
920
  [--logging verbose]
@@ -924,17 +926,18 @@ Each pair is written to `<out_dir>/<PairID>_<scName>__<stName>/`.
924
926
 
925
927
  ```
926
928
  <out_dir>/
927
- ├── reference_scaffold.h5ad # cached reference preparation, shared by all mappers
928
- └── <mapping>/ # one folder per mapper, e.g. nearest_centroid/
929
- ├── config.yaml # the run's settings
930
- ├── start_clustering.h5ad # each reference cell's start cluster
931
- ├── cell_shape_features.csv # only with a segmentation mask: area, eccentricity, solidity per cell
932
- ├── k_<kkk>/ # one folder per K
933
- │ ├── spot_to_state_mapping_soft.h5ad # the mapping P (spots × K), plus confidence if the mapper has one
934
- │ ├── start_cluster_to_state.csv # which state each start cluster belongs to
935
- │ └── analysis/data/ # the metrics for this K (the GUI plots them)
936
- ├── k_comparison.csv # one row per K: all metrics and the K-selection scores
937
- └── k_selection.json # the suggested K, overall and per criterion
929
+ ├── reference_scaffold.h5ad # cached reference preparation, shared by all starts and mappers
930
+ ├── cell_shape_features.csv # only with a segmentation mask: area, eccentricity, solidity per cell
931
+ └── <start>/ # leiden/, plus annotation_<obs_column>/ per --annotation
932
+ ├── start_clustering.h5ad # each reference cell's start cluster (shared by the mappers)
933
+ └── <mapping>/ # one folder per mapper, e.g. nearest_centroid/
934
+ ├── config.yaml # the run's settings (start_from_annotation: this start)
935
+ ├── k_<kkk>/ # one folder per K
936
+ │ ├── spot_to_state_mapping_soft.h5ad # the mapping P (spots × K), plus confidence if the mapper has one
937
+ │ ├── start_cluster_to_state.csv # which state each start cluster belongs to
938
+ │ └── analysis/ # the metrics for this K (the GUI plots them)
939
+ ├── k_comparison.csv # one row per K: all metrics and the K-selection scores
940
+ └── k_selection.json # the suggested K, overall and per criterion
938
941
  ```
939
942
 
940
943
  **Sample-dataset example:**
@@ -950,7 +953,7 @@ spatialaim run \
950
953
 
951
954
  ### Annotation-based baseline
952
955
 
953
- There is no separate command for it: `spatialaim run --start_from_annotation <obs_column>` covers it.
956
+ There is no separate command for it: `spatialaim run --annotation <obs_column>` covers it.
954
957
  The annotated types become the start clusters, and the sweep's **`K` = number-of-types** level is the baseline.
955
958
 
956
959
  ## For developers
@@ -1012,7 +1015,7 @@ ReferenceAligner("<name>", "<name>_env", "spatialaim.reference_aligners.run_<nam
1012
1015
  It is then selectable everywhere automatically:
1013
1016
 
1014
1017
  - `spatialaim gui` (it will be selectable in the sidebar)
1015
- - `spatialaim run --mapping <name>` (as an SpatialAIM reference mapper, one alignment per `K`) — including `--start_from_annotation`, where it maps onto a pre-existing annotation
1018
+ - `spatialaim run --mapping <name>` (as an SpatialAIM reference mapper, one alignment per `K`) — including the `--annotation` starts, where it maps onto a pre-existing annotation
1016
1019
 
1017
1020
  **4. Create PR**
1018
1021
 
@@ -22,8 +22,8 @@ The spot→state step is **modular**. In principle, any scRNA to ST alignment to
22
22
  We implement two baseline mappers (`nearest_centroid`, `wann`) and three reference mappers (`tangram`, `tacco`, `dot`).
23
23
  You are invited to add and try out your mapper of choice!
24
24
 
25
- If you want to replace the Leiden overclustering from Step 1 with your own annotations,
26
- `--start_from_annotation` takes an existing annotation's `L` cell types as the start clusters instead.
25
+ If you also want to start Step 1 from your own annotations,
26
+ `--annotation` runs the sweep a second time (once per annotation) with an existing annotation's `L` cell types as the start clusters.
27
27
 
28
28
  ![aim_figure](./docs/figure_2_1.png)
29
29
 
@@ -139,8 +139,10 @@ spatialaim gui [--server_port 8501]
139
139
 
140
140
  Open the printed URL (default http://localhost:8501), set the inputs, pick one or multiple mapper(s), and click **Run**.
141
141
  As soon as both `.h5ad` paths are set, the GUI runs the same checks as [`spatialaim validate`](#validate-a-dataset-pair-spatialaim-validate) on that pair: a clean pair is confirmed with a tick in the sidebar, any error or warning is shown in a dialog you can close to continue anyway.
142
- The GUI writes each mapper's sweep to `<output_dir>/<mapper>/`. The results using each mapper are visualized in different tabs. When having run multiple mappers, also a "Compare" tab appears.
143
- An optional **Segmentation path** (the ST slice's cell segmentation mask, see the sidebar's *Info*) is validated with the pair and passed to the runs; their mapper tabs then get a "Shape features" card (shape consistency and the cells' area / eccentricity / solidity per cell type), and the cell-type modal a "Shape features" view. Without it the GUI is unchanged.
142
+ The GUI always runs from the Leiden over-clustering; pick annotations under *Also start from annotations* to additionally start from them (the same as `--annotation`).
143
+ It writes the same layout as the CLI, `<output_dir>/<start>/<mapper>/`. The results are pages in a top navigation: **Overview** (a table of every starting point × mapper at its best overall K, sorted by the overall score; a selected row opens it in Explore), **Explore** (pick starting clusters, method and K in one control bar; switching start or method lands on that combination's best K), **Compare** (several methods of one starting point side by side, once two have finished) and **Single-cell reference**.
144
+ Once an output folder holds results, its linkage is fixed. Mappers and annotations can still be added: the next *Run* computes every missing starting point × mapper combination. Output folders written by earlier versions (`<output_dir>/<mapper>/`) can still be opened and viewed.
145
+ An optional **Segmentation path** (the ST slice's cell segmentation mask, see the sidebar's *Info*) is validated with the pair and passed to the runs; their Explore views then get a "Shape features" card (shape consistency and the cells' area / eccentricity / solidity per cell type), and the cell-type modal a "Shape features" view. Without it the GUI is unchanged.
144
146
 
145
147
  ### Run via CLI (`spatialaim run`)
146
148
 
@@ -156,10 +158,10 @@ The linkage of the agglomeration tree over the start clusters is chosen with `--
156
158
  - **`average`** (default): UPGMA — average pairwise distance; no size term, tends to peel small tight groups off a growing dominant state.
157
159
  - **`ward`**: Ward's criterion (R's `ward.D`); carries a size term and tends to produce balanced states.
158
160
 
159
- The **start clusters** the tree is built over come from the Leiden over-clustering by default.
160
- Pass `--start_from_annotation <obs_column>` to use a pre-existing annotation instead: its cell types become the start clusters, no over-clustering is computed at all,
161
- and `K` sweeps from the number of annotated types down (cells with no label are dropped).
162
- Give such runs their own `--output_dir`: a run root is named after the mapper alone, so the two modes would otherwise overwrite each other.
161
+ The **start clusters** the tree is built over come from the Leiden over-clustering.
162
+ Pass `--annotation <obs_column> [<obs_column> ...]` to additionally start from pre-existing annotations: the sweep then runs once from the Leiden clusters
163
+ and once per annotation, whose cell types become the start clusters (`K` sweeps from the number of annotated types down).
164
+ Every annotation must label every cell; a missing column or an unlabelled cell is an error (in batch mode, that pair fails).
163
165
 
164
166
  **Single pair**
165
167
 
@@ -170,7 +172,7 @@ spatialaim run --scdata path/to/sc.h5ad \
170
172
  [--segmentation path/to/mask.tif] \
171
173
  [--mapping nearest_centroid|wann|tangram|tacco|dot] \
172
174
  [--linkage_method average|ward] \
173
- [--start_from_annotation <obs_column>] \
175
+ [--annotation <obs_column> ...] \
174
176
  [--leiden_resolution 3.0] \
175
177
  [--k_min 1] [--k_max <L>] [--k_step 1] \
176
178
  [--logging verbose]
@@ -187,7 +189,7 @@ spatialaim run --pairs_csv path/to/pairs.csv \
187
189
  --output_dir path/to/out_dir \
188
190
  [--mapping nearest_centroid|wann|tangram|tacco|dot] \
189
191
  [--linkage_method average|ward] \
190
- [--start_from_annotation <obs_column>] \
192
+ [--annotation <obs_column> ...] \
191
193
  [--leiden_resolution 3.0] \
192
194
  [--k_min 1] [--k_max <L>] [--k_step 1] \
193
195
  [--logging verbose]
@@ -199,17 +201,18 @@ Each pair is written to `<out_dir>/<PairID>_<scName>__<stName>/`.
199
201
 
200
202
  ```
201
203
  <out_dir>/
202
- ├── reference_scaffold.h5ad # cached reference preparation, shared by all mappers
203
- └── <mapping>/ # one folder per mapper, e.g. nearest_centroid/
204
- ├── config.yaml # the run's settings
205
- ├── start_clustering.h5ad # each reference cell's start cluster
206
- ├── cell_shape_features.csv # only with a segmentation mask: area, eccentricity, solidity per cell
207
- ├── k_<kkk>/ # one folder per K
208
- │ ├── spot_to_state_mapping_soft.h5ad # the mapping P (spots × K), plus confidence if the mapper has one
209
- │ ├── start_cluster_to_state.csv # which state each start cluster belongs to
210
- │ └── analysis/data/ # the metrics for this K (the GUI plots them)
211
- ├── k_comparison.csv # one row per K: all metrics and the K-selection scores
212
- └── k_selection.json # the suggested K, overall and per criterion
204
+ ├── reference_scaffold.h5ad # cached reference preparation, shared by all starts and mappers
205
+ ├── cell_shape_features.csv # only with a segmentation mask: area, eccentricity, solidity per cell
206
+ └── <start>/ # leiden/, plus annotation_<obs_column>/ per --annotation
207
+ ├── start_clustering.h5ad # each reference cell's start cluster (shared by the mappers)
208
+ └── <mapping>/ # one folder per mapper, e.g. nearest_centroid/
209
+ ├── config.yaml # the run's settings (start_from_annotation: this start)
210
+ ├── k_<kkk>/ # one folder per K
211
+ │ ├── spot_to_state_mapping_soft.h5ad # the mapping P (spots × K), plus confidence if the mapper has one
212
+ │ ├── start_cluster_to_state.csv # which state each start cluster belongs to
213
+ │ └── analysis/ # the metrics for this K (the GUI plots them)
214
+ ├── k_comparison.csv # one row per K: all metrics and the K-selection scores
215
+ └── k_selection.json # the suggested K, overall and per criterion
213
216
  ```
214
217
 
215
218
  **Sample-dataset example:**
@@ -225,7 +228,7 @@ spatialaim run \
225
228
 
226
229
  ### Annotation-based baseline
227
230
 
228
- There is no separate command for it: `spatialaim run --start_from_annotation <obs_column>` covers it.
231
+ There is no separate command for it: `spatialaim run --annotation <obs_column>` covers it.
229
232
  The annotated types become the start clusters, and the sweep's **`K` = number-of-types** level is the baseline.
230
233
 
231
234
  ## For developers
@@ -287,7 +290,7 @@ ReferenceAligner("<name>", "<name>_env", "spatialaim.reference_aligners.run_<nam
287
290
  It is then selectable everywhere automatically:
288
291
 
289
292
  - `spatialaim gui` (it will be selectable in the sidebar)
290
- - `spatialaim run --mapping <name>` (as an SpatialAIM reference mapper, one alignment per `K`) — including `--start_from_annotation`, where it maps onto a pre-existing annotation
293
+ - `spatialaim run --mapping <name>` (as an SpatialAIM reference mapper, one alignment per `K`) — including the `--annotation` starts, where it maps onto a pre-existing annotation
291
294
 
292
295
  **4. Create PR**
293
296
 
@@ -27,7 +27,7 @@ dependencies:
27
27
  # --- core (pip-only) ---
28
28
  - igraph # modularity (metrics.modularity)
29
29
  - leidenalg # Leiden over-clustering
30
- - squidpy # spatial metrics (analysis.spatial_organization)
30
+ - squidpy # spatial metrics (analysis.topology)
31
31
  # --- GUI only ---
32
32
  - streamlit
33
33
  - plotly
@@ -48,7 +48,7 @@ dependencies = [
48
48
  "zarr>=2.16", # reading large masks in bands
49
49
  "scikit-image>=0.22", # shape-consistency metric (regionprops), rasterising masks
50
50
  # GUI
51
- "streamlit>=1.40",
51
+ "streamlit>=1.61", # persist_state (sidebar survives st.switch_page)
52
52
  "plotly>=5.20",
53
53
  "kaleido>=0.2"
54
54
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatial-aim
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: SpatialAIM — annotation-independent mapping of scRNA-seq references onto high-resolution spatial transcriptomics
5
5
  Author-email: Dominik Pysch <dominik.pysch@fau.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -705,7 +705,7 @@ Requires-Dist: umap-learn>=0.5
705
705
  Requires-Dist: tifffile>=2024.8
706
706
  Requires-Dist: zarr>=2.16
707
707
  Requires-Dist: scikit-image>=0.22
708
- Requires-Dist: streamlit>=1.40
708
+ Requires-Dist: streamlit>=1.61
709
709
  Requires-Dist: plotly>=5.20
710
710
  Requires-Dist: kaleido>=0.2
711
711
  Provides-Extra: data
@@ -747,8 +747,8 @@ The spot→state step is **modular**. In principle, any scRNA to ST alignment to
747
747
  We implement two baseline mappers (`nearest_centroid`, `wann`) and three reference mappers (`tangram`, `tacco`, `dot`).
748
748
  You are invited to add and try out your mapper of choice!
749
749
 
750
- If you want to replace the Leiden overclustering from Step 1 with your own annotations,
751
- `--start_from_annotation` takes an existing annotation's `L` cell types as the start clusters instead.
750
+ If you also want to start Step 1 from your own annotations,
751
+ `--annotation` runs the sweep a second time (once per annotation) with an existing annotation's `L` cell types as the start clusters.
752
752
 
753
753
  ![aim_figure](./docs/figure_2_1.png)
754
754
 
@@ -864,8 +864,10 @@ spatialaim gui [--server_port 8501]
864
864
 
865
865
  Open the printed URL (default http://localhost:8501), set the inputs, pick one or multiple mapper(s), and click **Run**.
866
866
  As soon as both `.h5ad` paths are set, the GUI runs the same checks as [`spatialaim validate`](#validate-a-dataset-pair-spatialaim-validate) on that pair: a clean pair is confirmed with a tick in the sidebar, any error or warning is shown in a dialog you can close to continue anyway.
867
- The GUI writes each mapper's sweep to `<output_dir>/<mapper>/`. The results using each mapper are visualized in different tabs. When having run multiple mappers, also a "Compare" tab appears.
868
- An optional **Segmentation path** (the ST slice's cell segmentation mask, see the sidebar's *Info*) is validated with the pair and passed to the runs; their mapper tabs then get a "Shape features" card (shape consistency and the cells' area / eccentricity / solidity per cell type), and the cell-type modal a "Shape features" view. Without it the GUI is unchanged.
867
+ The GUI always runs from the Leiden over-clustering; pick annotations under *Also start from annotations* to additionally start from them (the same as `--annotation`).
868
+ It writes the same layout as the CLI, `<output_dir>/<start>/<mapper>/`. The results are pages in a top navigation: **Overview** (a table of every starting point × mapper at its best overall K, sorted by the overall score; a selected row opens it in Explore), **Explore** (pick starting clusters, method and K in one control bar; switching start or method lands on that combination's best K), **Compare** (several methods of one starting point side by side, once two have finished) and **Single-cell reference**.
869
+ Once an output folder holds results, its linkage is fixed. Mappers and annotations can still be added: the next *Run* computes every missing starting point × mapper combination. Output folders written by earlier versions (`<output_dir>/<mapper>/`) can still be opened and viewed.
870
+ An optional **Segmentation path** (the ST slice's cell segmentation mask, see the sidebar's *Info*) is validated with the pair and passed to the runs; their Explore views then get a "Shape features" card (shape consistency and the cells' area / eccentricity / solidity per cell type), and the cell-type modal a "Shape features" view. Without it the GUI is unchanged.
869
871
 
870
872
  ### Run via CLI (`spatialaim run`)
871
873
 
@@ -881,10 +883,10 @@ The linkage of the agglomeration tree over the start clusters is chosen with `--
881
883
  - **`average`** (default): UPGMA — average pairwise distance; no size term, tends to peel small tight groups off a growing dominant state.
882
884
  - **`ward`**: Ward's criterion (R's `ward.D`); carries a size term and tends to produce balanced states.
883
885
 
884
- The **start clusters** the tree is built over come from the Leiden over-clustering by default.
885
- Pass `--start_from_annotation <obs_column>` to use a pre-existing annotation instead: its cell types become the start clusters, no over-clustering is computed at all,
886
- and `K` sweeps from the number of annotated types down (cells with no label are dropped).
887
- Give such runs their own `--output_dir`: a run root is named after the mapper alone, so the two modes would otherwise overwrite each other.
886
+ The **start clusters** the tree is built over come from the Leiden over-clustering.
887
+ Pass `--annotation <obs_column> [<obs_column> ...]` to additionally start from pre-existing annotations: the sweep then runs once from the Leiden clusters
888
+ and once per annotation, whose cell types become the start clusters (`K` sweeps from the number of annotated types down).
889
+ Every annotation must label every cell; a missing column or an unlabelled cell is an error (in batch mode, that pair fails).
888
890
 
889
891
  **Single pair**
890
892
 
@@ -895,7 +897,7 @@ spatialaim run --scdata path/to/sc.h5ad \
895
897
  [--segmentation path/to/mask.tif] \
896
898
  [--mapping nearest_centroid|wann|tangram|tacco|dot] \
897
899
  [--linkage_method average|ward] \
898
- [--start_from_annotation <obs_column>] \
900
+ [--annotation <obs_column> ...] \
899
901
  [--leiden_resolution 3.0] \
900
902
  [--k_min 1] [--k_max <L>] [--k_step 1] \
901
903
  [--logging verbose]
@@ -912,7 +914,7 @@ spatialaim run --pairs_csv path/to/pairs.csv \
912
914
  --output_dir path/to/out_dir \
913
915
  [--mapping nearest_centroid|wann|tangram|tacco|dot] \
914
916
  [--linkage_method average|ward] \
915
- [--start_from_annotation <obs_column>] \
917
+ [--annotation <obs_column> ...] \
916
918
  [--leiden_resolution 3.0] \
917
919
  [--k_min 1] [--k_max <L>] [--k_step 1] \
918
920
  [--logging verbose]
@@ -924,17 +926,18 @@ Each pair is written to `<out_dir>/<PairID>_<scName>__<stName>/`.
924
926
 
925
927
  ```
926
928
  <out_dir>/
927
- ├── reference_scaffold.h5ad # cached reference preparation, shared by all mappers
928
- └── <mapping>/ # one folder per mapper, e.g. nearest_centroid/
929
- ├── config.yaml # the run's settings
930
- ├── start_clustering.h5ad # each reference cell's start cluster
931
- ├── cell_shape_features.csv # only with a segmentation mask: area, eccentricity, solidity per cell
932
- ├── k_<kkk>/ # one folder per K
933
- │ ├── spot_to_state_mapping_soft.h5ad # the mapping P (spots × K), plus confidence if the mapper has one
934
- │ ├── start_cluster_to_state.csv # which state each start cluster belongs to
935
- │ └── analysis/data/ # the metrics for this K (the GUI plots them)
936
- ├── k_comparison.csv # one row per K: all metrics and the K-selection scores
937
- └── k_selection.json # the suggested K, overall and per criterion
929
+ ├── reference_scaffold.h5ad # cached reference preparation, shared by all starts and mappers
930
+ ├── cell_shape_features.csv # only with a segmentation mask: area, eccentricity, solidity per cell
931
+ └── <start>/ # leiden/, plus annotation_<obs_column>/ per --annotation
932
+ ├── start_clustering.h5ad # each reference cell's start cluster (shared by the mappers)
933
+ └── <mapping>/ # one folder per mapper, e.g. nearest_centroid/
934
+ ├── config.yaml # the run's settings (start_from_annotation: this start)
935
+ ├── k_<kkk>/ # one folder per K
936
+ │ ├── spot_to_state_mapping_soft.h5ad # the mapping P (spots × K), plus confidence if the mapper has one
937
+ │ ├── start_cluster_to_state.csv # which state each start cluster belongs to
938
+ │ └── analysis/ # the metrics for this K (the GUI plots them)
939
+ ├── k_comparison.csv # one row per K: all metrics and the K-selection scores
940
+ └── k_selection.json # the suggested K, overall and per criterion
938
941
  ```
939
942
 
940
943
  **Sample-dataset example:**
@@ -950,7 +953,7 @@ spatialaim run \
950
953
 
951
954
  ### Annotation-based baseline
952
955
 
953
- There is no separate command for it: `spatialaim run --start_from_annotation <obs_column>` covers it.
956
+ There is no separate command for it: `spatialaim run --annotation <obs_column>` covers it.
954
957
  The annotated types become the start clusters, and the sweep's **`K` = number-of-types** level is the baseline.
955
958
 
956
959
  ## For developers
@@ -1012,7 +1015,7 @@ ReferenceAligner("<name>", "<name>_env", "spatialaim.reference_aligners.run_<nam
1012
1015
  It is then selectable everywhere automatically:
1013
1016
 
1014
1017
  - `spatialaim gui` (it will be selectable in the sidebar)
1015
- - `spatialaim run --mapping <name>` (as an SpatialAIM reference mapper, one alignment per `K`) — including `--start_from_annotation`, where it maps onto a pre-existing annotation
1018
+ - `spatialaim run --mapping <name>` (as an SpatialAIM reference mapper, one alignment per `K`) — including the `--annotation` starts, where it maps onto a pre-existing annotation
1016
1019
 
1017
1020
  **4. Create PR**
1018
1021
 
@@ -39,7 +39,7 @@ src/spatialaim/analysis/loading.py
39
39
  src/spatialaim/analysis/reconstruction.py
40
40
  src/spatialaim/analysis/run.py
41
41
  src/spatialaim/analysis/shape_consistency.py
42
- src/spatialaim/analysis/spatial_organization.py
42
+ src/spatialaim/analysis/topology.py
43
43
  src/spatialaim/analysis/compare_ks/__init__.py
44
44
  src/spatialaim/analysis/compare_ks/collect.py
45
45
  src/spatialaim/analysis/compare_ks/criteria.py
@@ -97,6 +97,8 @@ src/spatialaim/gui/views/choose_k.py
97
97
  src/spatialaim/gui/views/compare_tab.py
98
98
  src/spatialaim/gui/views/controls.py
99
99
  src/spatialaim/gui/views/mapper_tab.py
100
+ src/spatialaim/gui/views/overview.py
101
+ src/spatialaim/gui/views/pages.py
100
102
  src/spatialaim/gui/views/reference_tab.py
101
103
  src/spatialaim/gui/views/report.py
102
104
  src/spatialaim/gui/widgets/__init__.py
@@ -126,5 +128,8 @@ src/spatialaim/reference_aligners/run_tacco.py
126
128
  src/spatialaim/reference_aligners/run_tangram.py
127
129
  tests/test_cli.py
128
130
  tests/test_gui_imports.py
131
+ tests/test_gui_overview.py
129
132
  tests/test_registry.py
130
- tests/test_sweep_sample.py
133
+ tests/test_sweep_sample.py
134
+ tests/test_topology.py
135
+ tests/test_utils.py
@@ -11,7 +11,7 @@ umap-learn>=0.5
11
11
  tifffile>=2024.8
12
12
  zarr>=2.16
13
13
  scikit-image>=0.22
14
- streamlit>=1.40
14
+ streamlit>=1.61
15
15
  plotly>=5.20
16
16
  kaleido>=0.2
17
17
 
@@ -32,7 +32,7 @@
32
32
  "src/spatialaim/analysis/reconstruction.py",
33
33
  "src/spatialaim/analysis/run.py",
34
34
  "src/spatialaim/analysis/shape_consistency.py",
35
- "src/spatialaim/analysis/spatial_organization.py",
35
+ "src/spatialaim/analysis/topology.py",
36
36
  "src/spatialaim/cli.py",
37
37
  "src/spatialaim/clustering.py",
38
38
  "src/spatialaim/config.py",
@@ -87,6 +87,8 @@
87
87
  "src/spatialaim/gui/views/compare_tab.py",
88
88
  "src/spatialaim/gui/views/controls.py",
89
89
  "src/spatialaim/gui/views/mapper_tab.py",
90
+ "src/spatialaim/gui/views/overview.py",
91
+ "src/spatialaim/gui/views/pages.py",
90
92
  "src/spatialaim/gui/views/reference_tab.py",
91
93
  "src/spatialaim/gui/views/report.py",
92
94
  "src/spatialaim/gui/widgets/__init__.py",
@@ -120,7 +122,10 @@
120
122
  "src/spatialaim/utils.py",
121
123
  "tests/test_cli.py",
122
124
  "tests/test_gui_imports.py",
125
+ "tests/test_gui_overview.py",
123
126
  "tests/test_registry.py",
124
- "tests/test_sweep_sample.py"
127
+ "tests/test_sweep_sample.py",
128
+ "tests/test_topology.py",
129
+ "tests/test_utils.py"
125
130
  ]
126
131
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.4.0",
3
+ "distance": 0,
4
+ "node": "g7f082dce40f94cc4775514abd354f0448788e940",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-30"
8
+ }
@@ -8,9 +8,12 @@ Convention for the sc/st AnnData objects.
8
8
  # -------------------- adata_sc --------------------
9
9
 
10
10
 
11
- # (n_cells,) int — start-cluster label (0..L-1): the all-gene Leiden over-cluster, or the annotated type in start-from-annotation mode
11
+ # (n_cells,) int — start-cluster label (0..L-1) of the current start: the all-gene Leiden over-cluster, or the annotated type for an annotation start
12
12
  OBS_START_CLUSTER = "start_cluster"
13
13
 
14
+ # (n_cells,) int — all-gene Leiden over-cluster label; part of the start-independent scaffold, copied into OBS_START_CLUSTER for the Leiden start
15
+ OBS_LEIDEN_ALL_GENES = "leiden_all_genes"
16
+
14
17
  # (n_cells,) int — Leiden label from shared genes only
15
18
  OBS_LEIDEN_SHARED_GENES = "leiden_shared_genes"
16
19
 
@@ -23,7 +26,7 @@ UNS_N_START_CLUSTERS = "n_start_clusters"
23
26
  # list[str] — display name per start cluster, index-aligned to OBS_START_CLUSTER: the annotated type names, or "cluster_<i>" in Leiden mode
24
27
  UNS_START_CLUSTER_NAMES = "start_cluster_names"
25
28
 
26
- # float — resolution of the all-gene Leiden over-clustering (absent in start-from-annotation mode)
29
+ # float — resolution of the all-gene Leiden over-clustering
27
30
  UNS_LEIDEN_RESOLUTION_ALL_GENES = "leiden_resolution"
28
31
 
29
32
  # float — resolution (shared genes)
@@ -90,7 +93,7 @@ OBS_MAPPING_CONFIDENCE = "mapping_confidence"
90
93
 
91
94
  # obsm/obsp/uns — K-independent spatial/expression graphs, built once and reused across the K-sweep
92
95
 
93
- # (n_spots x n_spots) sparse — squidpy spatial KNN graph (local purity + neighbourhood enrichment)
96
+ # (n_spots x n_spots) sparse — squidpy spatial KNN graph (the spatial assortativity)
94
97
  OBSP_SPATIAL_CONNECTIVITIES = "spatial_connectivities"
95
98
 
96
99
  # (n_spots x n_spots) sparse — ST all-gene expression KNN graph for the mapping modularity
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Transcriptional coherence of one K's mapping: modularities of the states on the
3
- reference and ST expression graphs, and the per-state coherence for the GUI.
3
+ reference and ST expression graphs.
4
4
  """
5
5
 
6
6
  from __future__ import annotations
@@ -8,7 +8,6 @@ import json
8
8
  import logging
9
9
  from pathlib import Path
10
10
  import numpy as np
11
- import pandas as pd
12
11
  import scanpy as sc
13
12
  from anndata import AnnData
14
13
  from spatialaim.adata_schema import (
@@ -28,12 +27,9 @@ def analyse_modularities(
28
27
  adata_st: AnnData,
29
28
  output_data_dir: Path,
30
29
  ):
31
- """Write ``modularity_metrics.json`` and ``coherence_per_spot.csv``.
32
-
33
- The modularity of the states on the reference's all-gene and shared-gene
34
- kNN graphs and on the ST expression kNN graph, plus per state the chance
35
- level of its coherence (see :func:`_per_state_coherence`).
36
- """
30
+ """Write ``modularity_metrics.json``: the modularity of the states on the
31
+ reference's all-gene and shared-gene kNN graphs and on the ST expression
32
+ kNN graph."""
37
33
  cell_states = adata_sc.obs[OBS_COMPUTED_STATE].astype(int).to_numpy()
38
34
 
39
35
  modularity_all = compute_modularity(adata_sc, cell_states)
@@ -41,7 +37,6 @@ def analyse_modularities(
41
37
  adata_sc, cell_states, obsp_key=OBSP_CONNECTIVITIES_SHARED_GENES
42
38
  )
43
39
  modularity_st_expression = _spatial_expression_modularity(adata_st)
44
- per_state = _per_state_coherence(adata_st, cell_states, output_data_dir)
45
40
 
46
41
  with open(output_data_dir / "modularity_metrics.json", "w") as f:
47
42
  json.dump(
@@ -49,7 +44,6 @@ def analyse_modularities(
49
44
  "modularity_all": float(modularity_all),
50
45
  "modularity_shared": float(modularity_shared),
51
46
  "modularity_st_expression": float(modularity_st_expression),
52
- "per_state": per_state,
53
47
  },
54
48
  f,
55
49
  indent=4,
@@ -78,37 +72,3 @@ def _spatial_expression_modularity(adata_st: AnnData) -> float:
78
72
  )
79
73
  hard_labels = adata_st.obs[OBS_MAPPING_HARD].astype(int).to_numpy()
80
74
  return compute_modularity(graph, hard_labels)
81
-
82
-
83
- def _per_state_coherence(
84
- adata_st: AnnData, cell_states: np.ndarray, output_data_dir: Path
85
- ) -> dict:
86
- """Per spot, the share of its ST-expression neighbours mapped to its own
87
- state (written to ``coherence_per_spot.csv``). Returns per state its chance
88
- level, the state's share of spots; every reference state gets an entry,
89
- also one without spots."""
90
- hard = adata_st.obs[OBS_MAPPING_HARD].astype(int).to_numpy()
91
- share = _same_label_share(hard, adata_st.obsp[OBSP_ST_EXPR_CONNECTIVITIES])
92
- pd.DataFrame({"id": adata_st.obs_names, "same_label_share": share}).to_csv(
93
- output_data_dir / "coherence_per_spot.csv", index=False
94
- )
95
- return {
96
- str(int(s)): {"chance": float(np.mean(hard == s))}
97
- for s in np.union1d(np.unique(hard), np.unique(cell_states))
98
- }
99
-
100
-
101
- def _same_label_share(labels: np.ndarray, connectivities) -> np.ndarray:
102
- """Per node, the share of its graph neighbours with its own label (NaN
103
- without neighbours). Neighbours are the stored entries of its row, weights
104
- ignored, itself excluded."""
105
- A = connectivities.tocoo()
106
- off = A.row != A.col
107
- row, col = A.row[off], A.col[off]
108
- n = A.shape[0]
109
- same = np.bincount(
110
- row, weights=(labels[row] == labels[col]).astype(np.float64), minlength=n
111
- ).astype(np.int64)
112
- degree = np.bincount(row, minlength=n).astype(np.int64)
113
- with np.errstate(divide="ignore", invalid="ignore"):
114
- return np.where(degree > 0, same / degree, np.nan)
@@ -1,6 +1,6 @@
1
1
  from .criteria import CRITERIA, CRITERION, NULL_SUFFIX, SCATTER_PAIRS, Criterion
2
2
  from .run import compare_k_runs
3
- from .scoring import harmonic_mean, overall, scaled_curves, score_table
3
+ from .scoring import curves, harmonic_mean, overall, score_table
4
4
  from .selection import best_ks, pareto_mask
5
5
 
6
6
  __all__ = [
@@ -11,9 +11,9 @@ __all__ = [
11
11
  "Criterion",
12
12
  "best_ks",
13
13
  "compare_k_runs",
14
+ "curves",
14
15
  "harmonic_mean",
15
16
  "overall",
16
17
  "pareto_mask",
17
- "scaled_curves",
18
18
  "score_table",
19
19
  ]