spatial-aim 1.5.2__tar.gz → 1.5.4__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 (151) hide show
  1. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/PKG-INFO +1 -1
  2. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/deploy/demo/Dockerfile +5 -2
  3. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/deploy/demo/README.md +1 -1
  4. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/PKG-INFO +1 -1
  5. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/SOURCES.txt +5 -0
  6. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/scm_file_list.json +5 -0
  7. spatial_aim-1.5.4/src/spatial_aim.egg-info/scm_version.json +8 -0
  8. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/README.md +13 -0
  9. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/_segmentation.py +2 -1
  10. spatial_aim-1.5.4/src/spatialaim/data/curate_datasets/curate_11_human_crc.py +338 -0
  11. spatial_aim-1.5.4/src/spatialaim/data/curate_datasets/curate_12_human_ovarian_cancer.py +293 -0
  12. spatial_aim-1.5.4/src/spatialaim/data/curate_datasets/curate_13_human_carotid_plaque.py +205 -0
  13. spatial_aim-1.5.4/src/spatialaim/data/curate_datasets/curate_14_mouse_kidney_iri.py +242 -0
  14. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/app.py +6 -11
  15. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/demo.py +17 -2
  16. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/cell_type.py +50 -0
  17. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/common.py +3 -2
  18. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/markers.py +26 -8
  19. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/sidebar.py +13 -3
  20. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/cell_type_modal.py +13 -12
  21. spatial_aim-1.5.4/src/spatialaim/gui/views/gene_modal.py +89 -0
  22. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/mapper_tab.py +72 -25
  23. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/reference_tab.py +1 -1
  24. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/widgets/headline_plot.py +53 -8
  25. spatial_aim-1.5.2/src/spatial_aim.egg-info/scm_version.json +0 -8
  26. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/.dockerignore +0 -0
  27. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/.github/workflows/ci.yml +0 -0
  28. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/.github/workflows/docker-demo.yml +0 -0
  29. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/.github/workflows/release.yml +0 -0
  30. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/.gitignore +0 -0
  31. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/.pre-commit-config.yaml +0 -0
  32. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/LICENSE +0 -0
  33. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/README.md +0 -0
  34. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/deploy/demo/legal/imprint.md +0 -0
  35. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/deploy/demo/legal/privacy.md +0 -0
  36. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/docs/aim_gui.gif +0 -0
  37. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/docs/figure_2_1.png +0 -0
  38. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/environment.yml +0 -0
  39. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/pyproject.toml +0 -0
  40. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/renovate.json +0 -0
  41. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/sample_dataset/ST/index.csv +0 -0
  42. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/sample_dataset/ST/sample_st.h5ad +0 -0
  43. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/sample_dataset/pairs.csv +0 -0
  44. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/sample_dataset/scRNA/index.csv +0 -0
  45. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/sample_dataset/scRNA/sample_sc.h5ad +0 -0
  46. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/setup.cfg +0 -0
  47. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/dependency_links.txt +0 -0
  48. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/entry_points.txt +0 -0
  49. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/requires.txt +0 -0
  50. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatial_aim.egg-info/top_level.txt +0 -0
  51. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/__init__.py +0 -0
  52. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/__main__.py +0 -0
  53. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/adata_schema.py +0 -0
  54. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/aggregation.py +0 -0
  55. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/__init__.py +0 -0
  56. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/coherence.py +0 -0
  57. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/compare_ks/__init__.py +0 -0
  58. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/compare_ks/collect.py +0 -0
  59. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/compare_ks/criteria.py +0 -0
  60. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/compare_ks/run.py +0 -0
  61. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/compare_ks/scoring.py +0 -0
  62. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/compare_ks/selection.py +0 -0
  63. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/loading.py +0 -0
  64. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/reconstruction.py +0 -0
  65. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/run.py +0 -0
  66. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/shape_consistency.py +0 -0
  67. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/analysis/topology.py +0 -0
  68. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/cli.py +0 -0
  69. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/clustering.py +0 -0
  70. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/config.py +0 -0
  71. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/__init__.py +0 -0
  72. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/__init__.py +0 -0
  73. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/_common.py +0 -0
  74. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_01_mouse_ssp.py +0 -0
  75. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_02_colorectal_cancer.py +0 -0
  76. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_03_mouse_liver.py +0 -0
  77. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_04_mouse_kidney.py +0 -0
  78. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_05_mouse_embryo.py +0 -0
  79. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_06_mouse_retina.py +0 -0
  80. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_07_mouse_mpfc.py +0 -0
  81. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_08_mouse_visual_cortex.py +0 -0
  82. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_09_human_breast_cancer.py +0 -0
  83. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/curate_10_human_mtg.py +0 -0
  84. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/dump_retina_counts.R +0 -0
  85. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/curate_datasets/environment_retina_qs.yml +0 -0
  86. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/__init__.py +0 -0
  87. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/common.py +0 -0
  88. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/runner.py +0 -0
  89. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/validate_pair.py +0 -0
  90. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/validate_sc.py +0 -0
  91. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/validate_segmentation.py +0 -0
  92. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/data/validate/validate_st.py +0 -0
  93. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/__init__.py +0 -0
  94. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/__main__.py +0 -0
  95. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/compute.py +0 -0
  96. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/data_access.py +0 -0
  97. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/loaders.py +0 -0
  98. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/__init__.py +0 -0
  99. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/headline.py +0 -0
  100. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/ksweep.py +0 -0
  101. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/report.py +0 -0
  102. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/scatter.py +0 -0
  103. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/render/shape.py +0 -0
  104. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/scaffold.py +0 -0
  105. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/state_names.py +0 -0
  106. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/texts.py +0 -0
  107. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/validation.py +0 -0
  108. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/__init__.py +0 -0
  109. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/cards.py +0 -0
  110. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/choose_k.py +0 -0
  111. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/compare_tab.py +0 -0
  112. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/controls.py +0 -0
  113. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/overview.py +0 -0
  114. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/pages.py +0 -0
  115. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/views/report.py +0 -0
  116. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/widgets/__init__.py +0 -0
  117. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/widgets/linked_plot.py +0 -0
  118. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/widgets/plotly_fit.py +0 -0
  119. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/gui/widgets/slider.py +0 -0
  120. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/io.py +0 -0
  121. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/mapping/__init__.py +0 -0
  122. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/mapping/base.py +0 -0
  123. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/mapping/confidence.py +0 -0
  124. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/mapping/nearest_centroid.py +0 -0
  125. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/mapping/reference.py +0 -0
  126. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/mapping/wann.py +0 -0
  127. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/metrics/__init__.py +0 -0
  128. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/metrics/cossim.py +0 -0
  129. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/metrics/modularity.py +0 -0
  130. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/metrics/shape_consistency.py +0 -0
  131. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/metrics/topology.py +0 -0
  132. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/__init__.py +0 -0
  133. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/environment_dot.yml +0 -0
  134. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/environment_tacco.yml +0 -0
  135. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/environment_tangram.yml +0 -0
  136. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/registry.py +0 -0
  137. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/run_dot.R +0 -0
  138. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/run_dot.py +0 -0
  139. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/run_tacco.py +0 -0
  140. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/reference_aligners/run_tangram.py +0 -0
  141. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/sweep.py +0 -0
  142. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/timing.py +0 -0
  143. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/tree.py +0 -0
  144. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/src/spatialaim/utils.py +0 -0
  145. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_cli.py +0 -0
  146. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_gui_imports.py +0 -0
  147. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_gui_overview.py +0 -0
  148. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_registry.py +0 -0
  149. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_sweep_sample.py +0 -0
  150. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_topology.py +0 -0
  151. {spatial_aim-1.5.2 → spatial_aim-1.5.4}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatial-aim
3
- Version: 1.5.2
3
+ Version: 1.5.4
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
@@ -36,8 +36,11 @@ COPY deploy/demo/legal ./legal
36
36
 
37
37
  RUN useradd --create-home --uid 10001 spatialaim
38
38
  USER spatialaim
39
- # numba (via scanpy/umap) caches compiled code next to the sources by default.
40
- ENV NUMBA_CACHE_DIR=/tmp/numba \
39
+ # The container runs read-only with only /tmp writable: numba (via scanpy/umap)
40
+ # caches compiled code next to the sources by default, and anything else that
41
+ # writes to the home folder (~/.streamlit, ~/.cache) lands in /tmp too.
42
+ ENV HOME=/tmp \
43
+ NUMBA_CACHE_DIR=/tmp/numba \
41
44
  MPLCONFIGDIR=/tmp/matplotlib
42
45
 
43
46
  EXPOSE 8501
@@ -68,7 +68,7 @@ workflow run). The pages are linked from the sidebar footer on every page (`/imp
68
68
  ## 4. Run
69
69
 
70
70
  ```bash
71
- docker run -d --name spatialaim-demo --restart unless-stopped -p 127.0.0.1:8501:8501 -v /srv/spatialaim-demo/data:/data:ro --read-only --tmpfs /tmp --tmpfs /home/spatialaim --memory 16g --cpus 4 ghcr.io/domipysch/spatialaim-demo:latest
71
+ docker run -d --name spatialaim-demo --restart unless-stopped -p 127.0.0.1:8501:8501 -v /testmount2/zi69hebi/spatialaim-demo/data:/data:ro --read-only --tmpfs /tmp --memory 16g --cpus 4 ghcr.io/domipysch/spatialaim-demo:latest
72
72
  ```
73
73
 
74
74
  The container serves plain HTTP on port 8501; `127.0.0.1:` keeps it reachable only for the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatial-aim
3
- Version: 1.5.2
3
+ Version: 1.5.4
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
@@ -68,6 +68,10 @@ src/spatialaim/data/curate_datasets/curate_07_mouse_mpfc.py
68
68
  src/spatialaim/data/curate_datasets/curate_08_mouse_visual_cortex.py
69
69
  src/spatialaim/data/curate_datasets/curate_09_human_breast_cancer.py
70
70
  src/spatialaim/data/curate_datasets/curate_10_human_mtg.py
71
+ src/spatialaim/data/curate_datasets/curate_11_human_crc.py
72
+ src/spatialaim/data/curate_datasets/curate_12_human_ovarian_cancer.py
73
+ src/spatialaim/data/curate_datasets/curate_13_human_carotid_plaque.py
74
+ src/spatialaim/data/curate_datasets/curate_14_mouse_kidney_iri.py
71
75
  src/spatialaim/data/curate_datasets/dump_retina_counts.R
72
76
  src/spatialaim/data/curate_datasets/environment_retina_qs.yml
73
77
  src/spatialaim/data/validate/__init__.py
@@ -104,6 +108,7 @@ src/spatialaim/gui/views/cell_type_modal.py
104
108
  src/spatialaim/gui/views/choose_k.py
105
109
  src/spatialaim/gui/views/compare_tab.py
106
110
  src/spatialaim/gui/views/controls.py
111
+ src/spatialaim/gui/views/gene_modal.py
107
112
  src/spatialaim/gui/views/mapper_tab.py
108
113
  src/spatialaim/gui/views/overview.py
109
114
  src/spatialaim/gui/views/pages.py
@@ -57,6 +57,10 @@
57
57
  "src/spatialaim/data/curate_datasets/curate_08_mouse_visual_cortex.py",
58
58
  "src/spatialaim/data/curate_datasets/curate_09_human_breast_cancer.py",
59
59
  "src/spatialaim/data/curate_datasets/curate_10_human_mtg.py",
60
+ "src/spatialaim/data/curate_datasets/curate_11_human_crc.py",
61
+ "src/spatialaim/data/curate_datasets/curate_12_human_ovarian_cancer.py",
62
+ "src/spatialaim/data/curate_datasets/curate_13_human_carotid_plaque.py",
63
+ "src/spatialaim/data/curate_datasets/curate_14_mouse_kidney_iri.py",
60
64
  "src/spatialaim/data/curate_datasets/dump_retina_counts.R",
61
65
  "src/spatialaim/data/curate_datasets/environment_retina_qs.yml",
62
66
  "src/spatialaim/data/validate/__init__.py",
@@ -93,6 +97,7 @@
93
97
  "src/spatialaim/gui/views/choose_k.py",
94
98
  "src/spatialaim/gui/views/compare_tab.py",
95
99
  "src/spatialaim/gui/views/controls.py",
100
+ "src/spatialaim/gui/views/gene_modal.py",
96
101
  "src/spatialaim/gui/views/mapper_tab.py",
97
102
  "src/spatialaim/gui/views/overview.py",
98
103
  "src/spatialaim/gui/views/pages.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.5.4",
3
+ "distance": 0,
4
+ "node": "gdd5395362e0c4b4e871535e0cebf530345d63a2d",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-11-09"
8
+ }
@@ -12,3 +12,16 @@ Only minor pre-processing (deleting near-empty columns etc.) has been applied.
12
12
  No normalization or log operations are applied to the data, as we are working on raw counts.
13
13
 
14
14
  These 10 script are fully generated by AI (Claude Opus 5.5).
15
+
16
+ ## GUI use-case candidates (11-14)
17
+
18
+ `curate_11` to `curate_14` build candidate pairs for the GUI section (scRNA + imaging-based ST, all
19
+ Xenium with segmentation masks). They write to `Data/03_GUI`, not to `01_Datasets_Paper`, and are
20
+ not part of Experiment 1.
21
+
22
+ | Script | Pair(s) | Sources |
23
+ |---|---|---|
24
+ | `curate_11_human_crc.py` | `11_human-crc-p2`, `11_human-crc-p5` | Oliveira et al. 2025, Flex + 10x Xenium |
25
+ | `curate_12_human_ovarian_cancer.py` | `12_human-ovarian-cancer` (3 x 3 mm crop) | 10x Flex + Xenium Prime 5K |
26
+ | `curate_13_human_carotid_plaque.py` | `13_human-carotid-plaque` | Nat Commun 2025, Zenodo scRNA + GEO Xenium |
27
+ | `curate_14_mouse_kidney_iri.py` | `14_mouse-kidney-iri` | Kirita et al. 2020 snRNA + Humphreys lab Xenium |
@@ -300,7 +300,8 @@ def extract_remote_zip_members(
300
300
  info = next(
301
301
  i
302
302
  for i in archive.infolist()
303
- if i.filename.endswith("/" + name) and "__MACOSX" not in i.filename
303
+ if (i.filename == name or i.filename.endswith("/" + name))
304
+ and "__MACOSX" not in i.filename
304
305
  )
305
306
  logger.info(
306
307
  " extract %s (%.0f MB compressed) from %s",
@@ -0,0 +1,338 @@
1
+ """
2
+ Build dataset 11_human-crc: two patient-matched pairs (P2, P5), each a Flex
3
+ reference + one Xenium slice, for the GUI use case (Results section 5). Not part
4
+ of the Experiment 1 corpus, so it is written to Data/03_GUI by default, not to
5
+ 01_Datasets_Paper.
6
+
7
+ Pairs covered (one per patient, same_paper):
8
+ 11_human-crc-p2 <-> 11_human-crc-p2
9
+ 11_human-crc-p5 <-> 11_human-crc-p5
10
+
11
+ Paper: Oliveira et al., Nature Genetics 2025, "High-definition spatial
12
+ transcriptomic profiling of immune cell populations in colorectal cancer"
13
+ (https://doi.org/10.1038/s41588-025-02193-3; GEO SuperSeries GSE280318). Per
14
+ patient, both sides come from one FFPE block: the Flex reference was dissociated
15
+ from curls serial to the sections the spatial assays used.
16
+
17
+ Downloads per patient: four Flex libraries, the authors' metadata and three
18
+ Xenium files (P2 ~460 MB, P5 ~330 MB; the segmentation is most of it).
19
+
20
+ ------------------------------------------------------------------------------
21
+ scRNA 11_human-crc-p{2,5}.h5ad — Chromium Flex
22
+ ------------------------------------------------------------------------------
23
+ counts GSE280311 (Flex). Each patient is one probe barcode in four
24
+ libraries: P2CRC = BC1 in pool 1 (GSM8594533..36, *_P1L{1..4}_*),
25
+ P5CRC = BC3 in pool 2 (GSM8594537..40, *_P2L{1..4}_*);
26
+ files *_count_filtered_feature_bc_matrix.h5
27
+ annotation github.com/10XGenomics/HumanColonCancer_VisiumHD,
28
+ MetaData/SingleCell_MetaData.csv.gz (all 8 Flex samples)
29
+
30
+ The authors' barcodes carry the cellranger-aggr index as suffix (pool 1: two
31
+ samples per library, pool 2: four), so P2CRC's libraries are aggr samples
32
+ 1, 3, 5, 7 and P5CRC's 11, 15, 19, 23. A patient's barcodes match its four
33
+ matrices one to one; the authors' QC (``QCFilter == "Keep"``) selects the cells,
34
+ kept in metadata order: P2 41933 -> 35673, P5 24615 -> 23823.
35
+
36
+ Curation:
37
+ * obs index = the authors' ``Barcode``; ``Level1`` and ``Level2`` verbatim.
38
+ Level2 was sub-clustered within Level1 on a 15 % sketch and then
39
+ extended to all cells, so it is only mostly nested (P2: 3.8 % of
40
+ cells sit outside their Level2's main Level1); kept as published.
41
+ * X = the four matrices' columns for those barcodes, float32
42
+ * var index = gene symbols uppercased; three symbols occur twice and are
43
+ summed onto the first occurrence, 18082 -> 18079
44
+
45
+ ------------------------------------------------------------------------------
46
+ ST 11_human-crc-p{2,5}.h5ad — Xenium, 422 genes (colon panel + 100 add-on)
47
+ ------------------------------------------------------------------------------
48
+ https://cf.10xgenomics.com/samples/xenium/2.0.0/
49
+ Xenium_V1_Human_Colon_Cancer_P{2,5}_CRC_Add_on_FFPE/ (the GSE280314 samples)
50
+ ..._cell_feature_matrix.h5, ..._cells.csv.gz, ..._cells.zarr.zip
51
+
52
+ The full outs bundles (14-16 GB) are unnecessary. The authors' repo lists ~1.1M
53
+ cells per slide for a larger Xenium region; the public 10x releases used here
54
+ have P2 340837 and P5 275998 cells and carry no cell-type labels.
55
+
56
+ Curation:
57
+ * cells in the Xenium output's own order; obs index = Xenium ``cell_id``
58
+ * cells with fewer than 10 transcripts over the 422 genes are dropped
59
+ (P2 340837 -> 331383, P5 275998 -> 270956): mapped, they form
60
+ spurious states of near-empty cells
61
+ * X = the 422 ``Gene Expression`` features; unassigned / negative control
62
+ codewords and probes are dropped
63
+ * var index = the feature names uppercased
64
+ * obsm["spatial"] = ``x_centroid``, ``y_centroid`` (um), float32
65
+ * no cell-type column: the paper annotated Visium HD bins, not Xenium cells
66
+ * obs["segmentation_id"], uns["segmentation_pixel_size_um"] = 0.2125 and the
67
+ mask ST/Segmentation/<name>.tif: cells.zarr.zip masks/1, copied as
68
+ is. Its labels are the row of the zarr ``cell_id`` array plus one,
69
+ which is the cells.csv order, so segmentation_id = that row + 1
70
+ (the dropped cells' labels stay in the mask). Xenium 2.0
71
+ multimodal segmentation (boundary stain, then interior stain, then
72
+ 5 um nucleus expansion).
73
+
74
+ ------------------------------------------------------------------------------
75
+ Usage
76
+ ------------------------------------------------------------------------------
77
+ python -m spatialaim.data.curate_datasets.curate_11_human_crc
78
+ python -m spatialaim.data.curate_datasets.curate_11_human_crc --out-dir <root>
79
+
80
+ Sources are downloaded to a temporary folder below --out-dir and deleted again.
81
+ Set SPATIALAIM_KEEP_DOWNLOADS=1 to keep (and on a re-run reuse) them in
82
+ <out-dir>/_downloads/11_human-crc-p{2,5} instead. The 4-5 GB masks are copied
83
+ band by band, never held whole.
84
+ """
85
+
86
+ import logging
87
+ from dataclasses import dataclass
88
+ from pathlib import Path
89
+ from typing import Any
90
+
91
+ import anndata as ad
92
+ import numpy as np
93
+ import pandas as pd
94
+ from scipy.sparse import hstack
95
+
96
+ from . import _common, _segmentation
97
+ from .curate_09_human_breast_cancer import (
98
+ GENE_EXPRESSION,
99
+ MASK_PIXEL_UM,
100
+ _check_shape,
101
+ _copy_cell_mask,
102
+ _fold_duplicates,
103
+ _read_10x_h5,
104
+ )
105
+
106
+ logger = logging.getLogger(__name__)
107
+
108
+ OUT_ROOT = _common.DATA_ROOT / "03_GUI"
109
+
110
+ GEO_SAMPLES = "https://ftp.ncbi.nlm.nih.gov/geo/samples/GSM8594nnn"
111
+ REPO = "https://raw.githubusercontent.com/10XGenomics/HumanColonCancer_VisiumHD/main"
112
+ SC_META_URL = f"{REPO}/MetaData/SingleCell_MetaData.csv.gz"
113
+ SC_META_BYTES = 7_288_481
114
+ XENIUM = "https://cf.10xgenomics.com/samples/xenium/2.0.0"
115
+ SC_GENES = 18079
116
+ ST_GENES = 422
117
+ MIN_TRANSCRIPTS = 10
118
+
119
+
120
+ @dataclass(frozen=True)
121
+ class Patient:
122
+ patient: str # the metadata's Patient value, e.g. "P5CRC"
123
+ probe_barcode: str
124
+ # (GSM, library, aggr index of this patient in the authors' barcodes, bytes)
125
+ libraries: tuple[tuple[str, str, int, int], ...]
126
+ n_cells: int
127
+ n_spots: int # all Xenium cells
128
+ n_spots_kept: int # with >= MIN_TRANSCRIPTS
129
+ # bytes of cell_feature_matrix.h5, cells.csv.gz, cells.zarr.zip
130
+ xenium_bytes: tuple[int, int, int]
131
+
132
+ @property
133
+ def name(self) -> str:
134
+ return f"11_human-crc-{self.patient[:2].lower()}"
135
+
136
+ @property
137
+ def xenium_prefix(self) -> str:
138
+ sample = f"Xenium_V1_Human_Colon_Cancer_{self.patient[:2]}_CRC_Add_on_FFPE"
139
+ return f"{XENIUM}/{sample}/{sample}"
140
+
141
+
142
+ PATIENTS = (
143
+ Patient(
144
+ patient="P2CRC",
145
+ probe_barcode="BC1",
146
+ libraries=(
147
+ ("GSM8594533", "P1L1", 1, 26_874_789),
148
+ ("GSM8594534", "P1L2", 3, 27_306_204),
149
+ ("GSM8594535", "P1L3", 5, 27_294_024),
150
+ ("GSM8594536", "P1L4", 7, 26_361_551),
151
+ ),
152
+ n_cells=35673,
153
+ n_spots=340837,
154
+ n_spots_kept=331383,
155
+ xenium_bytes=(21_438_841, 18_562_765, 291_928_061),
156
+ ),
157
+ Patient(
158
+ patient="P5CRC",
159
+ probe_barcode="BC3",
160
+ libraries=(
161
+ ("GSM8594537", "P2L1", 11, 9_062_161),
162
+ ("GSM8594538", "P2L2", 15, 9_081_608),
163
+ ("GSM8594539", "P2L3", 19, 9_784_770),
164
+ ("GSM8594540", "P2L4", 23, 9_630_123),
165
+ ),
166
+ n_cells=23823,
167
+ n_spots=275998,
168
+ n_spots_kept=270956,
169
+ xenium_bytes=(15_479_659, 14_803_142, 248_276_261),
170
+ ),
171
+ )
172
+
173
+
174
+ # --------------------------------------------------------------------------- #
175
+ # scRNA
176
+ # --------------------------------------------------------------------------- #
177
+ def build_scrna(p: Patient, tmp_dir: Path, out_path: Path) -> None:
178
+ logger.info("scRNA %s — Chromium Flex, patient %s (GSE280311)", p.name, p.patient)
179
+ meta_file = _common.download(
180
+ SC_META_URL,
181
+ tmp_dir / "SingleCell_MetaData.csv.gz",
182
+ expected_bytes=SC_META_BYTES,
183
+ )
184
+ meta = pd.read_csv(meta_file)
185
+ meta = meta[meta["Patient"] == p.patient]
186
+ logger.info(" read metadata (%d %s barcodes)", len(meta), p.patient)
187
+
188
+ blocks = []
189
+ barcodes: list[str] = []
190
+ names: np.ndarray | None = None
191
+ for gsm, library, aggr, size in p.libraries:
192
+ file_name = (
193
+ f"{gsm}_{library}_{p.patient}_{p.probe_barcode}"
194
+ "_count_filtered_feature_bc_matrix.h5"
195
+ )
196
+ path = _common.download(
197
+ f"{GEO_SAMPLES}/{gsm}/suppl/{file_name}",
198
+ tmp_dir / file_name,
199
+ expected_bytes=size,
200
+ )
201
+ matrix, library_barcodes, library_names, _ = _read_10x_h5(path)
202
+ if names is not None and not np.array_equal(names, library_names):
203
+ raise ValueError(f"{file_name}: gene list differs from the first library")
204
+ names = library_names
205
+ blocks.append(matrix)
206
+ barcodes.extend(b.split("-")[0] + f"-{aggr}" for b in library_barcodes)
207
+ if names is None:
208
+ raise ValueError(f"{p.patient}: no libraries listed")
209
+ matrix = hstack(blocks).tocsc()
210
+ logger.info(" read %d libraries (%d x %d)", len(blocks), *matrix.shape)
211
+
212
+ if set(barcodes) != set(meta["Barcode"]):
213
+ raise ValueError("the four libraries no longer match the metadata's barcodes")
214
+ meta = meta[meta["QCFilter"] == "Keep"]
215
+ position = pd.Index(barcodes).get_indexer(meta["Barcode"].to_numpy())
216
+
217
+ X = matrix[:, position].T.tocsr().astype(np.float32)
218
+ X, genes = _fold_duplicates(X, np.asarray(_common.uppercase(names)))
219
+ logger.info(" genes %d -> %d (duplicate symbols summed)", len(names), len(genes))
220
+
221
+ adata = ad.AnnData(
222
+ X=X,
223
+ obs=pd.DataFrame(
224
+ {
225
+ level: pd.Categorical(meta[level].astype(str).to_numpy())
226
+ for level in ("Level1", "Level2")
227
+ },
228
+ index=pd.Index(meta["Barcode"].to_numpy(), name=None),
229
+ ),
230
+ var=pd.DataFrame(index=pd.Index(genes, name=None)),
231
+ )
232
+ _check_shape(adata, (p.n_cells, SC_GENES), "scRNA")
233
+ for level in ("Level1", "Level2"):
234
+ logger.info(" %s: %d types", level, adata.obs[level].cat.categories.size)
235
+ _common.write(adata, out_path)
236
+
237
+
238
+ # --------------------------------------------------------------------------- #
239
+ # ST
240
+ # --------------------------------------------------------------------------- #
241
+ def build_st(p: Patient, tmp_dir: Path, st_dir: Path) -> None:
242
+ logger.info(
243
+ "ST %s — Xenium human colon cancer %s (GSE280314)", p.name, p.patient[:2]
244
+ )
245
+ tag = f"Xenium_{p.patient[:2]}"
246
+ counts_bytes, cells_bytes, masks_bytes = p.xenium_bytes
247
+ counts_file = _common.download(
248
+ f"{p.xenium_prefix}_cell_feature_matrix.h5",
249
+ tmp_dir / f"{tag}_cell_feature_matrix.h5",
250
+ expected_bytes=counts_bytes,
251
+ )
252
+ cells_file = _common.download(
253
+ f"{p.xenium_prefix}_cells.csv.gz",
254
+ tmp_dir / f"{tag}_cells.csv.gz",
255
+ expected_bytes=cells_bytes,
256
+ )
257
+ masks_file = _common.download(
258
+ f"{p.xenium_prefix}_cells.zarr.zip",
259
+ tmp_dir / f"{tag}_cells.zarr.zip",
260
+ expected_bytes=masks_bytes,
261
+ )
262
+
263
+ matrix, barcodes, names, types = _read_10x_h5(counts_file)
264
+ genes = types == GENE_EXPRESSION
265
+ logger.info(
266
+ " read matrix (%d x %d, %d gene features)", *matrix.shape, int(genes.sum())
267
+ )
268
+
269
+ cells = pd.read_csv(cells_file)
270
+ if not np.array_equal(cells["cell_id"].astype(str).to_numpy(), barcodes):
271
+ raise ValueError("cells.csv.gz and the matrix no longer share a cell order")
272
+
273
+ adata = ad.AnnData(
274
+ X=matrix[genes].T.tocsr().astype(np.float32),
275
+ obs=pd.DataFrame(index=pd.Index(barcodes, name=None)),
276
+ var=pd.DataFrame(index=pd.Index(_common.uppercase(names[genes]), name=None)),
277
+ )
278
+ adata.obsm["spatial"] = np.column_stack(
279
+ [cells["x_centroid"].to_numpy(), cells["y_centroid"].to_numpy()]
280
+ ).astype(np.float32)
281
+ _check_shape(adata, (p.n_spots, ST_GENES), "ST")
282
+
283
+ _check_mask_order(masks_file, barcodes)
284
+ _copy_cell_mask(masks_file, _segmentation.mask_path(st_dir, p.name), adata.n_obs)
285
+ _segmentation.annotate(adata, np.arange(1, adata.n_obs + 1), MASK_PIXEL_UM)
286
+
287
+ keep = np.asarray(adata.X.sum(axis=1)).ravel() >= MIN_TRANSCRIPTS
288
+ adata = adata[keep].copy()
289
+ logger.info(
290
+ " cells %d -> %d (>= %d transcripts)",
291
+ len(keep),
292
+ adata.n_obs,
293
+ MIN_TRANSCRIPTS,
294
+ )
295
+ _check_shape(adata, (p.n_spots_kept, ST_GENES), "ST")
296
+ _common.write(adata, st_dir / f"{p.name}.h5ad")
297
+
298
+
299
+ def _check_mask_order(zarr_zip: Path, barcodes: np.ndarray) -> None:
300
+ """Mask label i+1 is the zarr's cell_id row i; assert that row order = ours."""
301
+ import zarr
302
+
303
+ group: Any = zarr.open_group(
304
+ zarr.storage.ZipStore(str(zarr_zip), mode="r"), mode="r"
305
+ )
306
+ rows = np.asarray(group["cell_id"][:])
307
+ # Xenium string ids: the prefix integer in base 16 written with letters a..p
308
+ decoded = np.array(
309
+ [
310
+ [int(prefix.translate(_HEX_LETTERS), 16), int(suffix)]
311
+ for prefix, suffix in (b.split("-") for b in barcodes)
312
+ ],
313
+ dtype=rows.dtype,
314
+ )
315
+ if not np.array_equal(decoded, rows):
316
+ raise ValueError("the mask's cell_id rows no longer follow the cells.csv order")
317
+
318
+
319
+ _HEX_LETTERS = str.maketrans("abcdefghijklmnop", "0123456789abcdef")
320
+
321
+
322
+ # --------------------------------------------------------------------------- #
323
+ def main() -> None:
324
+ _common.setup_logging()
325
+ parser = _common.build_parser(__doc__)
326
+ parser.set_defaults(out_dir=OUT_ROOT)
327
+ args = parser.parse_args()
328
+ sc_dir, st_dir = _common.prepare_dirs(args.out_dir)
329
+
330
+ for p in PATIENTS:
331
+ with _common.staging(args.out_dir, p.name) as tmp_dir:
332
+ build_scrna(p, tmp_dir, sc_dir / f"{p.name}.h5ad")
333
+ build_st(p, tmp_dir, st_dir)
334
+ logger.info("%s done", p.name)
335
+
336
+
337
+ if __name__ == "__main__":
338
+ main()