sphere-pca 0.3.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 (72) hide show
  1. sphere_pca-0.3.0/CITATION.cff +34 -0
  2. sphere_pca-0.3.0/LICENSE +21 -0
  3. sphere_pca-0.3.0/MANIFEST.in +6 -0
  4. sphere_pca-0.3.0/PKG-INFO +216 -0
  5. sphere_pca-0.3.0/README.md +143 -0
  6. sphere_pca-0.3.0/assets/images/Fig1A_pipeline_schematic.png +0 -0
  7. sphere_pca-0.3.0/assets/images/Fig1B_sphere_example.png +0 -0
  8. sphere_pca-0.3.0/assets/images/SPHERE-PCA_logo.png +0 -0
  9. sphere_pca-0.3.0/assets/images/celegan_globe.png +0 -0
  10. sphere_pca-0.3.0/assets/images/celegan_polar.png +0 -0
  11. sphere_pca-0.3.0/assets/images/celegan_stripe.png +0 -0
  12. sphere_pca-0.3.0/assets/images/epi_globe.png +0 -0
  13. sphere_pca-0.3.0/assets/images/planarian.png +0 -0
  14. sphere_pca-0.3.0/assets/images/planarian_test.png +0 -0
  15. sphere_pca-0.3.0/examples/README.md +35 -0
  16. sphere_pca-0.3.0/examples/anndata_workflow.py +41 -0
  17. sphere_pca-0.3.0/examples/existing_pca.py +30 -0
  18. sphere_pca-0.3.0/pca_sphere_projection/__init__.py +215 -0
  19. sphere_pca-0.3.0/pca_sphere_projection/app.py +522 -0
  20. sphere_pca-0.3.0/pca_sphere_projection/app_plots.py +231 -0
  21. sphere_pca-0.3.0/pca_sphere_projection/app_processing.py +257 -0
  22. sphere_pca-0.3.0/pca_sphere_projection/app_report.py +91 -0
  23. sphere_pca-0.3.0/pca_sphere_projection/comparison.py +185 -0
  24. sphere_pca-0.3.0/pca_sphere_projection/core.py +589 -0
  25. sphere_pca-0.3.0/pca_sphere_projection/data/example_configs.yaml +8 -0
  26. sphere_pca-0.3.0/pca_sphere_projection/data/synthetic_pc_coordinates.csv +13 -0
  27. sphere_pca-0.3.0/pca_sphere_projection/entropy.py +197 -0
  28. sphere_pca-0.3.0/pca_sphere_projection/figures/__init__.py +32 -0
  29. sphere_pca-0.3.0/pca_sphere_projection/figures/common.py +571 -0
  30. sphere_pca-0.3.0/pca_sphere_projection/figures/fig1.py +628 -0
  31. sphere_pca-0.3.0/pca_sphere_projection/figures/fig2.py +872 -0
  32. sphere_pca-0.3.0/pca_sphere_projection/figures/fig3.py +1470 -0
  33. sphere_pca-0.3.0/pca_sphere_projection/figures/fig4.py +1390 -0
  34. sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/__init__.py +15 -0
  35. sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/benchmark.py +430 -0
  36. sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/common.py +156 -0
  37. sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/planaria.py +713 -0
  38. sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/xenium.py +546 -0
  39. sphere_pca-0.3.0/pca_sphere_projection/gene_geometry.py +370 -0
  40. sphere_pca-0.3.0/pca_sphere_projection/io.py +564 -0
  41. sphere_pca-0.3.0/pca_sphere_projection/known_regulators.py +116 -0
  42. sphere_pca-0.3.0/pca_sphere_projection/pc_robustness.py +115 -0
  43. sphere_pca-0.3.0/pca_sphere_projection/perturbation.py +458 -0
  44. sphere_pca-0.3.0/pca_sphere_projection/preprocessing.py +252 -0
  45. sphere_pca-0.3.0/pca_sphere_projection/robustness.py +260 -0
  46. sphere_pca-0.3.0/pca_sphere_projection/sphere_stats.py +312 -0
  47. sphere_pca-0.3.0/pca_sphere_projection/stripe.py +144 -0
  48. sphere_pca-0.3.0/pca_sphere_projection/topology.py +223 -0
  49. sphere_pca-0.3.0/pyproject.toml +109 -0
  50. sphere_pca-0.3.0/requirements-manuscript.txt +13 -0
  51. sphere_pca-0.3.0/scripts/manuscript/README.md +53 -0
  52. sphere_pca-0.3.0/scripts/manuscript/make_manuscript_figures.py +125 -0
  53. sphere_pca-0.3.0/scripts/manuscript/make_supplement_figures.py +398 -0
  54. sphere_pca-0.3.0/scripts/manuscript/smoke_test_celegan.py +40 -0
  55. sphere_pca-0.3.0/setup.cfg +4 -0
  56. sphere_pca-0.3.0/sphere_pca/__init__.py +14 -0
  57. sphere_pca-0.3.0/sphere_pca/cli.py +51 -0
  58. sphere_pca-0.3.0/sphere_pca/core.py +326 -0
  59. sphere_pca-0.3.0/sphere_pca/plotting.py +95 -0
  60. sphere_pca-0.3.0/sphere_pca/result.py +89 -0
  61. sphere_pca-0.3.0/sphere_pca.egg-info/PKG-INFO +216 -0
  62. sphere_pca-0.3.0/sphere_pca.egg-info/SOURCES.txt +70 -0
  63. sphere_pca-0.3.0/sphere_pca.egg-info/dependency_links.txt +1 -0
  64. sphere_pca-0.3.0/sphere_pca.egg-info/entry_points.txt +2 -0
  65. sphere_pca-0.3.0/sphere_pca.egg-info/requires.txt +54 -0
  66. sphere_pca-0.3.0/sphere_pca.egg-info/top_level.txt +2 -0
  67. sphere_pca-0.3.0/tests/test_comparison.py +53 -0
  68. sphere_pca-0.3.0/tests/test_perturbation.py +208 -0
  69. sphere_pca-0.3.0/tests/test_public_api.py +269 -0
  70. sphere_pca-0.3.0/tests/test_robustness.py +72 -0
  71. sphere_pca-0.3.0/tests/test_sphere_stats.py +73 -0
  72. sphere_pca-0.3.0/tests/test_topology.py +43 -0
@@ -0,0 +1,34 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use SPHERE-PCA, please cite the bioRxiv preprint."
3
+ title: "SPHERE-PCA"
4
+ type: software
5
+ version: 0.3.0
6
+ license: MIT
7
+ repository-code: "https://github.com/imlong4real/SPHERE-PCA"
8
+ url: "https://github.com/imlong4real/SPHERE-PCA"
9
+ authors:
10
+ - family-names: Yuan
11
+ given-names: Long
12
+ email: lyuan13@jhmi.edu
13
+ preferred-citation:
14
+ type: article
15
+ title: "Interpretable spherical geometry of single-cell state transitions from dominant principal components"
16
+ authors:
17
+ - family-names: Yuan
18
+ given-names: Long
19
+ - family-names: Li
20
+ given-names: Xuyang
21
+ - family-names: Le
22
+ given-names: My
23
+ - family-names: Hicks
24
+ given-names: Stephanie C.
25
+ - family-names: Deshpande
26
+ given-names: Atul
27
+ - family-names: Taube
28
+ given-names: Janis M.
29
+ - family-names: Szalay
30
+ given-names: Alexander S.
31
+ journal: bioRxiv
32
+ year: 2026
33
+ doi: 10.64898/2026.09.11.751061
34
+ url: "https://doi.org/10.64898/2026.09.11.751061"
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 imlong4real
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include CITATION.cff
2
+ include requirements-manuscript.txt
3
+ recursive-include assets/images *.png
4
+ recursive-include examples *.md *.py
5
+ recursive-include pca_sphere_projection/data *.csv *.yaml
6
+ recursive-include scripts/manuscript *.md *.py
@@ -0,0 +1,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: sphere-pca
3
+ Version: 0.3.0
4
+ Summary: Interpretable spherical coordinates for single-cell state transitions from principal components.
5
+ Author-email: Long Yuan <lyuan13@jhmi.edu>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/imlong4real/SPHERE-PCA
8
+ Project-URL: Repository, https://github.com/imlong4real/SPHERE-PCA
9
+ Project-URL: Issues, https://github.com/imlong4real/SPHERE-PCA/issues
10
+ Project-URL: Preprint, https://doi.org/10.64898/2026.09.11.751061
11
+ Keywords: single-cell,principal-component-analysis,spherical-geometry,trajectory-analysis
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: numpy>=1.23.5
25
+ Requires-Dist: scikit-learn>=1.2
26
+ Provides-Extra: plot
27
+ Requires-Dist: matplotlib>=3.6; extra == "plot"
28
+ Provides-Extra: singlecell
29
+ Requires-Dist: anndata>=0.9; extra == "singlecell"
30
+ Provides-Extra: legacy
31
+ Requires-Dist: matplotlib>=3.6; extra == "legacy"
32
+ Requires-Dist: pandas>=1.5; extra == "legacy"
33
+ Requires-Dist: plotly>=5.15; extra == "legacy"
34
+ Requires-Dist: scipy>=1.9; extra == "legacy"
35
+ Requires-Dist: seaborn>=0.12; extra == "legacy"
36
+ Requires-Dist: statsmodels>=0.14; extra == "legacy"
37
+ Provides-Extra: app
38
+ Requires-Dist: matplotlib>=3.6; extra == "app"
39
+ Requires-Dist: pandas>=1.5; extra == "app"
40
+ Requires-Dist: plotly>=5.15; extra == "app"
41
+ Requires-Dist: pyyaml>=6; extra == "app"
42
+ Requires-Dist: scipy>=1.9; extra == "app"
43
+ Requires-Dist: seaborn>=0.12; extra == "app"
44
+ Requires-Dist: statsmodels>=0.14; extra == "app"
45
+ Requires-Dist: streamlit>=1.30; extra == "app"
46
+ Provides-Extra: manuscript
47
+ Requires-Dist: anndata>=0.9; extra == "manuscript"
48
+ Requires-Dist: gseapy>=1.1; extra == "manuscript"
49
+ Requires-Dist: h5py>=3.8; extra == "manuscript"
50
+ Requires-Dist: matplotlib>=3.6; extra == "manuscript"
51
+ Requires-Dist: pandas>=1.5; extra == "manuscript"
52
+ Requires-Dist: plotly>=5.15; extra == "manuscript"
53
+ Requires-Dist: pyyaml>=6; extra == "manuscript"
54
+ Requires-Dist: rdata>=0.9; extra == "manuscript"
55
+ Requires-Dist: scanpy>=1.10; extra == "manuscript"
56
+ Requires-Dist: scipy>=1.9; extra == "manuscript"
57
+ Requires-Dist: seaborn>=0.12; extra == "manuscript"
58
+ Requires-Dist: statsmodels>=0.14; extra == "manuscript"
59
+ Requires-Dist: streamlit>=1.30; extra == "manuscript"
60
+ Provides-Extra: dev
61
+ Requires-Dist: anndata>=0.9; extra == "dev"
62
+ Requires-Dist: build>=1.2; extra == "dev"
63
+ Requires-Dist: matplotlib>=3.6; extra == "dev"
64
+ Requires-Dist: pandas>=1.5; extra == "dev"
65
+ Requires-Dist: plotly>=5.15; extra == "dev"
66
+ Requires-Dist: pytest>=7.4; extra == "dev"
67
+ Requires-Dist: pyyaml>=6; extra == "dev"
68
+ Requires-Dist: scipy>=1.9; extra == "dev"
69
+ Requires-Dist: seaborn>=0.12; extra == "dev"
70
+ Requires-Dist: statsmodels>=0.14; extra == "dev"
71
+ Requires-Dist: twine>=5; extra == "dev"
72
+ Dynamic: license-file
73
+
74
+ <div align="center">
75
+ <img src="assets/images/SPHERE-PCA_logo.png" alt="SPHERE-PCA logo" width="190">
76
+ <h1>SPHERE-PCA</h1>
77
+ <p><strong>Interpretable spherical coordinates for single-cell state transitions from principal components.</strong></p>
78
+
79
+ <p>
80
+ <a href="https://doi.org/10.64898/2026.09.11.751061"><img src="https://img.shields.io/badge/bioRxiv-10.64898%2F2026.09.11.751061-B31B1B" alt="bioRxiv DOI"></a>
81
+ <a href="https://github.com/imlong4real/SPHERE-PCA/actions/workflows/tests.yml"><img src="https://github.com/imlong4real/SPHERE-PCA/actions/workflows/tests.yml/badge.svg" alt="Tests"></a>
82
+ <a href="https://github.com/imlong4real/SPHERE-PCA/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
83
+ <img src="https://img.shields.io/badge/python-%E2%89%A53.9-3776AB" alt="Python 3.9 or newer">
84
+ <a href="https://academic.oup.com/jimmunol/article/214/Supplement_1/vkaf283.978/8332132"><img src="https://img.shields.io/badge/AAI%202025-Oral-6A5ACD" alt="AAI 2025 Oral"></a>
85
+ </p>
86
+ </div>
87
+
88
+ ## What is SPHERE-PCA?
89
+
90
+ SPHERE-PCA L2-normalizes PC1-PC3 onto the unit sphere, rotates a biologically
91
+ defined root population to the north pole, and returns three transparent
92
+ coordinates for every cell. It is deterministic, preserves the original PCA
93
+ loadings, and can start from either existing PC coordinates or a normalized
94
+ cell-by-gene matrix.
95
+
96
+ SPHERE-PCA is a geometric description—not automatic evidence of a causal
97
+ trajectory, developmental direction, or experimental perturbation response.
98
+ Root choice and coordinate interpretations should be validated independently.
99
+
100
+ ## Installation
101
+
102
+ Install the current source release:
103
+
104
+ ```bash
105
+ python -m pip install "sphere-pca[plot] @ git+https://github.com/imlong4real/SPHERE-PCA.git"
106
+ ```
107
+
108
+ For local development or manuscript reproduction:
109
+
110
+ ```bash
111
+ git clone https://github.com/imlong4real/SPHERE-PCA.git
112
+ cd SPHERE-PCA
113
+ python -m pip install -e ".[dev]"
114
+ ```
115
+
116
+ Optional extras are `plot`, `singlecell`, `app`, `manuscript`, and `dev`. The
117
+ core install contains only NumPy and scikit-learn. The preserved historical
118
+ namespace is available through the `legacy` extra; the dashboard uses `app`:
119
+
120
+ ```bash
121
+ python -m pip install "sphere-pca[app] @ git+https://github.com/imlong4real/SPHERE-PCA.git"
122
+ sphere-trace
123
+ ```
124
+
125
+ The installed dashboard includes a small synthetic example and accepts CSV
126
+ uploads. Large manuscript datasets are not included in the distribution.
127
+
128
+ ## Quick start
129
+
130
+ Transform existing PC1-PC3 coordinates:
131
+
132
+ ```python
133
+ import sphere_pca
134
+
135
+ result = sphere_pca.transform(pcs, root_mask=is_stem_cell)
136
+
137
+ # Or reuse an explicitly recorded centroid in PC1-PC3 coordinates:
138
+ result = sphere_pca.transform(pcs, root_centroid=published_centroid)
139
+
140
+ result.theta
141
+ result.phi
142
+ result.r
143
+ sphere_pca.plot(result, color=cell_type)
144
+ ```
145
+
146
+ Use an `AnnData` object with `adata.obsm["X_pca"]` (or pass `use_rep=None` to
147
+ fit PCA from `adata.X`):
148
+
149
+ ```python
150
+ result = sphere_pca.fit(
151
+ adata,
152
+ root="stem_cell",
153
+ root_key="cell_type",
154
+ write_back=True,
155
+ )
156
+ ```
157
+
158
+ This writes `sphere_pca_theta`, `sphere_pca_phi`, and `sphere_pca_r` to
159
+ `adata.obs` and aligned unit vectors to `adata.obsm["X_sphere_pca"]`.
160
+ `fit()` does not normalize counts, log-transform, select highly variable
161
+ genes, or scale features. Supply an existing PCA representation for exact
162
+ workflow control, or perform the required preprocessing before fitting PCA.
163
+
164
+ ## What the coordinates mean
165
+
166
+ | Coordinate | Meaning |
167
+ |---|---|
168
+ | **θ** | Root-aligned geodesic progression: angular distance from the chosen root direction. |
169
+ | **φ** | Angular state or branch position around the root-aligned sphere. |
170
+ | **r** | Pre-projection radial magnitude in PC1-PC3 space; it is retained, not normalized away. |
171
+
172
+ Angles are returned in radians. A structured coordinate can be biologically
173
+ useful without being causal; compare it with sampling time, lineage labels,
174
+ known markers, perturbations, or other independent evidence.
175
+
176
+ ## Tutorials / examples
177
+
178
+ Two download-free tutorials live in [`examples/`](examples/README.md):
179
+
180
+ - [`existing_pca.py`](examples/existing_pca.py) — transform and visualize a
181
+ tiny synthetic PC1-PC3 matrix.
182
+ - [`anndata_workflow.py`](examples/anndata_workflow.py) — fit from AnnData,
183
+ define a root population, and write coordinates back to `adata.obs`.
184
+
185
+ ## Reproducing the paper
186
+
187
+ The published analysis namespace remains available as
188
+ `pca_sphere_projection`; its numerical implementation has not been replaced by
189
+ the new public wrapper. The public API uses a proper orthogonal Rodrigues
190
+ rotation, while the frozen Figure 1 pathway retains its original implementation
191
+ for reproduction and can therefore produce different numerical coordinates.
192
+ Manuscript entry points and expected inputs are documented in
193
+ [`scripts/manuscript/`](scripts/manuscript/README.md).
194
+
195
+ ```bash
196
+ python -m pip install -r requirements-manuscript.txt
197
+ python scripts/manuscript/make_manuscript_figures.py --figure 1
198
+ ```
199
+
200
+ Large source datasets and generated outputs are intentionally excluded from
201
+ the Python distribution. Follow the preprint's data-access instructions and
202
+ place inputs under `raw_data/` before running the figure workflows.
203
+
204
+ ## Citation
205
+
206
+ If you use SPHERE-PCA, please cite:
207
+
208
+ > Yuan L, Li X, Le M, Hicks SC, Deshpande A, Taube JM, Szalay AS.
209
+ > *Interpretable spherical geometry of single-cell state transitions from dominant principal components.*
210
+ > bioRxiv (2026). <https://doi.org/10.64898/2026.09.11.751061>
211
+
212
+ Machine-readable metadata is available in [`CITATION.cff`](CITATION.cff).
213
+
214
+ ## License
215
+
216
+ SPHERE-PCA is released under the [MIT License](LICENSE).
@@ -0,0 +1,143 @@
1
+ <div align="center">
2
+ <img src="assets/images/SPHERE-PCA_logo.png" alt="SPHERE-PCA logo" width="190">
3
+ <h1>SPHERE-PCA</h1>
4
+ <p><strong>Interpretable spherical coordinates for single-cell state transitions from principal components.</strong></p>
5
+
6
+ <p>
7
+ <a href="https://doi.org/10.64898/2026.09.11.751061"><img src="https://img.shields.io/badge/bioRxiv-10.64898%2F2026.09.11.751061-B31B1B" alt="bioRxiv DOI"></a>
8
+ <a href="https://github.com/imlong4real/SPHERE-PCA/actions/workflows/tests.yml"><img src="https://github.com/imlong4real/SPHERE-PCA/actions/workflows/tests.yml/badge.svg" alt="Tests"></a>
9
+ <a href="https://github.com/imlong4real/SPHERE-PCA/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
10
+ <img src="https://img.shields.io/badge/python-%E2%89%A53.9-3776AB" alt="Python 3.9 or newer">
11
+ <a href="https://academic.oup.com/jimmunol/article/214/Supplement_1/vkaf283.978/8332132"><img src="https://img.shields.io/badge/AAI%202025-Oral-6A5ACD" alt="AAI 2025 Oral"></a>
12
+ </p>
13
+ </div>
14
+
15
+ ## What is SPHERE-PCA?
16
+
17
+ SPHERE-PCA L2-normalizes PC1-PC3 onto the unit sphere, rotates a biologically
18
+ defined root population to the north pole, and returns three transparent
19
+ coordinates for every cell. It is deterministic, preserves the original PCA
20
+ loadings, and can start from either existing PC coordinates or a normalized
21
+ cell-by-gene matrix.
22
+
23
+ SPHERE-PCA is a geometric description—not automatic evidence of a causal
24
+ trajectory, developmental direction, or experimental perturbation response.
25
+ Root choice and coordinate interpretations should be validated independently.
26
+
27
+ ## Installation
28
+
29
+ Install the current source release:
30
+
31
+ ```bash
32
+ python -m pip install "sphere-pca[plot] @ git+https://github.com/imlong4real/SPHERE-PCA.git"
33
+ ```
34
+
35
+ For local development or manuscript reproduction:
36
+
37
+ ```bash
38
+ git clone https://github.com/imlong4real/SPHERE-PCA.git
39
+ cd SPHERE-PCA
40
+ python -m pip install -e ".[dev]"
41
+ ```
42
+
43
+ Optional extras are `plot`, `singlecell`, `app`, `manuscript`, and `dev`. The
44
+ core install contains only NumPy and scikit-learn. The preserved historical
45
+ namespace is available through the `legacy` extra; the dashboard uses `app`:
46
+
47
+ ```bash
48
+ python -m pip install "sphere-pca[app] @ git+https://github.com/imlong4real/SPHERE-PCA.git"
49
+ sphere-trace
50
+ ```
51
+
52
+ The installed dashboard includes a small synthetic example and accepts CSV
53
+ uploads. Large manuscript datasets are not included in the distribution.
54
+
55
+ ## Quick start
56
+
57
+ Transform existing PC1-PC3 coordinates:
58
+
59
+ ```python
60
+ import sphere_pca
61
+
62
+ result = sphere_pca.transform(pcs, root_mask=is_stem_cell)
63
+
64
+ # Or reuse an explicitly recorded centroid in PC1-PC3 coordinates:
65
+ result = sphere_pca.transform(pcs, root_centroid=published_centroid)
66
+
67
+ result.theta
68
+ result.phi
69
+ result.r
70
+ sphere_pca.plot(result, color=cell_type)
71
+ ```
72
+
73
+ Use an `AnnData` object with `adata.obsm["X_pca"]` (or pass `use_rep=None` to
74
+ fit PCA from `adata.X`):
75
+
76
+ ```python
77
+ result = sphere_pca.fit(
78
+ adata,
79
+ root="stem_cell",
80
+ root_key="cell_type",
81
+ write_back=True,
82
+ )
83
+ ```
84
+
85
+ This writes `sphere_pca_theta`, `sphere_pca_phi`, and `sphere_pca_r` to
86
+ `adata.obs` and aligned unit vectors to `adata.obsm["X_sphere_pca"]`.
87
+ `fit()` does not normalize counts, log-transform, select highly variable
88
+ genes, or scale features. Supply an existing PCA representation for exact
89
+ workflow control, or perform the required preprocessing before fitting PCA.
90
+
91
+ ## What the coordinates mean
92
+
93
+ | Coordinate | Meaning |
94
+ |---|---|
95
+ | **θ** | Root-aligned geodesic progression: angular distance from the chosen root direction. |
96
+ | **φ** | Angular state or branch position around the root-aligned sphere. |
97
+ | **r** | Pre-projection radial magnitude in PC1-PC3 space; it is retained, not normalized away. |
98
+
99
+ Angles are returned in radians. A structured coordinate can be biologically
100
+ useful without being causal; compare it with sampling time, lineage labels,
101
+ known markers, perturbations, or other independent evidence.
102
+
103
+ ## Tutorials / examples
104
+
105
+ Two download-free tutorials live in [`examples/`](examples/README.md):
106
+
107
+ - [`existing_pca.py`](examples/existing_pca.py) — transform and visualize a
108
+ tiny synthetic PC1-PC3 matrix.
109
+ - [`anndata_workflow.py`](examples/anndata_workflow.py) — fit from AnnData,
110
+ define a root population, and write coordinates back to `adata.obs`.
111
+
112
+ ## Reproducing the paper
113
+
114
+ The published analysis namespace remains available as
115
+ `pca_sphere_projection`; its numerical implementation has not been replaced by
116
+ the new public wrapper. The public API uses a proper orthogonal Rodrigues
117
+ rotation, while the frozen Figure 1 pathway retains its original implementation
118
+ for reproduction and can therefore produce different numerical coordinates.
119
+ Manuscript entry points and expected inputs are documented in
120
+ [`scripts/manuscript/`](scripts/manuscript/README.md).
121
+
122
+ ```bash
123
+ python -m pip install -r requirements-manuscript.txt
124
+ python scripts/manuscript/make_manuscript_figures.py --figure 1
125
+ ```
126
+
127
+ Large source datasets and generated outputs are intentionally excluded from
128
+ the Python distribution. Follow the preprint's data-access instructions and
129
+ place inputs under `raw_data/` before running the figure workflows.
130
+
131
+ ## Citation
132
+
133
+ If you use SPHERE-PCA, please cite:
134
+
135
+ > Yuan L, Li X, Le M, Hicks SC, Deshpande A, Taube JM, Szalay AS.
136
+ > *Interpretable spherical geometry of single-cell state transitions from dominant principal components.*
137
+ > bioRxiv (2026). <https://doi.org/10.64898/2026.09.11.751061>
138
+
139
+ Machine-readable metadata is available in [`CITATION.cff`](CITATION.cff).
140
+
141
+ ## License
142
+
143
+ SPHERE-PCA is released under the [MIT License](LICENSE).
@@ -0,0 +1,35 @@
1
+ # Tutorials
2
+
3
+ These examples use tiny synthetic datasets, run in seconds, and require no
4
+ downloads.
5
+
6
+ ## 1. Existing PCA coordinates
7
+
8
+ Install plotting support and run:
9
+
10
+ ```bash
11
+ python -m pip install -e ".[plot]"
12
+ python examples/existing_pca.py
13
+ ```
14
+
15
+ The script constructs a small PC1-PC3 matrix, selects a root population,
16
+ computes `theta`, `phi`, and `r`, and saves an equirectangular view.
17
+
18
+ ## 2. AnnData
19
+
20
+ Install the single-cell and plotting extras and run:
21
+
22
+ ```bash
23
+ python -m pip install -e ".[singlecell,plot]"
24
+ python examples/anndata_workflow.py
25
+ ```
26
+
27
+ The script starts from a synthetic cell-by-gene `AnnData` object, fits PCA,
28
+ uses a known early-state population as the biological root, writes the three
29
+ coordinates to `adata.obs`, and saves a visualization. For a large or sparse
30
+ dataset, compute PCA with your normal single-cell workflow and place it in
31
+ `adata.obsm["X_pca"]`; `sphere_pca.fit` will use the first three columns.
32
+
33
+ Root choice is part of the scientific model. Use a population justified by
34
+ sampling time, markers, lineage knowledge, or another independent source—not
35
+ the spherical display alone.
@@ -0,0 +1,41 @@
1
+ """Tutorial 2: fit SPHERE-PCA from a small AnnData expression matrix."""
2
+
3
+ from pathlib import Path
4
+
5
+ import anndata as ad
6
+ import matplotlib.pyplot as plt
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+ import sphere_pca
11
+
12
+
13
+ rng = np.random.default_rng(12)
14
+ n_cells, n_genes = 60, 12
15
+ stage = np.repeat(["early", "middle", "late"], n_cells // 3)
16
+ signal = np.linspace(-2.0, 2.0, n_cells)
17
+ expression = rng.normal(0, 0.4, size=(n_cells, n_genes))
18
+ expression[:, 0:4] += signal[:, None]
19
+ expression[:, 4:8] += np.sin(signal[:, None])
20
+
21
+ adata = ad.AnnData(
22
+ X=expression,
23
+ obs=pd.DataFrame({"stage": stage}, index=[f"cell_{i}" for i in range(n_cells)]),
24
+ )
25
+
26
+ # Here use_rep=None asks SPHERE-PCA to fit PCA. In a typical Scanpy workflow,
27
+ # leave use_rep="X_pca" (the default) to reuse adata.obsm["X_pca"].
28
+ result = sphere_pca.fit(
29
+ adata,
30
+ root="early",
31
+ root_key="stage",
32
+ use_rep=None,
33
+ write_back=True,
34
+ )
35
+
36
+ print(adata.obs[["sphere_pca_theta", "sphere_pca_phi", "sphere_pca_r"]].head())
37
+ sphere_pca.plot(result, color=adata.obs["stage"], projection="equirectangular")
38
+ plt.tight_layout()
39
+ output = Path(__file__).with_name("anndata_workflow.png")
40
+ plt.savefig(output, dpi=160)
41
+ print(f"Saved {output}")
@@ -0,0 +1,30 @@
1
+ """Tutorial 1: transform existing PC1-PC3 coordinates."""
2
+
3
+ from pathlib import Path
4
+
5
+ import matplotlib.pyplot as plt
6
+ import numpy as np
7
+
8
+ import sphere_pca
9
+
10
+
11
+ rng = np.random.default_rng(4)
12
+ progress = np.linspace(0.15, 2.5, 80)
13
+ branch = np.repeat([-1.0, 1.0], 40)
14
+ pcs = np.column_stack(
15
+ (
16
+ np.sin(progress),
17
+ 0.35 * branch * np.sin(progress) + rng.normal(0, 0.04, progress.size),
18
+ np.cos(progress),
19
+ )
20
+ )
21
+ root_mask = progress < 0.35
22
+
23
+ result = sphere_pca.transform(pcs, root_mask=root_mask)
24
+ print(result.coordinates[:5]) # columns: theta, phi, r (angles in radians)
25
+
26
+ sphere_pca.plot(result, color=branch, projection="equirectangular")
27
+ plt.tight_layout()
28
+ output = Path(__file__).with_name("existing_pca.png")
29
+ plt.savefig(output, dpi=160)
30
+ print(f"Saved {output}")