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.
- sphere_pca-0.3.0/CITATION.cff +34 -0
- sphere_pca-0.3.0/LICENSE +21 -0
- sphere_pca-0.3.0/MANIFEST.in +6 -0
- sphere_pca-0.3.0/PKG-INFO +216 -0
- sphere_pca-0.3.0/README.md +143 -0
- sphere_pca-0.3.0/assets/images/Fig1A_pipeline_schematic.png +0 -0
- sphere_pca-0.3.0/assets/images/Fig1B_sphere_example.png +0 -0
- sphere_pca-0.3.0/assets/images/SPHERE-PCA_logo.png +0 -0
- sphere_pca-0.3.0/assets/images/celegan_globe.png +0 -0
- sphere_pca-0.3.0/assets/images/celegan_polar.png +0 -0
- sphere_pca-0.3.0/assets/images/celegan_stripe.png +0 -0
- sphere_pca-0.3.0/assets/images/epi_globe.png +0 -0
- sphere_pca-0.3.0/assets/images/planarian.png +0 -0
- sphere_pca-0.3.0/assets/images/planarian_test.png +0 -0
- sphere_pca-0.3.0/examples/README.md +35 -0
- sphere_pca-0.3.0/examples/anndata_workflow.py +41 -0
- sphere_pca-0.3.0/examples/existing_pca.py +30 -0
- sphere_pca-0.3.0/pca_sphere_projection/__init__.py +215 -0
- sphere_pca-0.3.0/pca_sphere_projection/app.py +522 -0
- sphere_pca-0.3.0/pca_sphere_projection/app_plots.py +231 -0
- sphere_pca-0.3.0/pca_sphere_projection/app_processing.py +257 -0
- sphere_pca-0.3.0/pca_sphere_projection/app_report.py +91 -0
- sphere_pca-0.3.0/pca_sphere_projection/comparison.py +185 -0
- sphere_pca-0.3.0/pca_sphere_projection/core.py +589 -0
- sphere_pca-0.3.0/pca_sphere_projection/data/example_configs.yaml +8 -0
- sphere_pca-0.3.0/pca_sphere_projection/data/synthetic_pc_coordinates.csv +13 -0
- sphere_pca-0.3.0/pca_sphere_projection/entropy.py +197 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/__init__.py +32 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/common.py +571 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/fig1.py +628 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/fig2.py +872 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/fig3.py +1470 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/fig4.py +1390 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/__init__.py +15 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/benchmark.py +430 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/common.py +156 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/planaria.py +713 -0
- sphere_pca-0.3.0/pca_sphere_projection/figures/supplements/xenium.py +546 -0
- sphere_pca-0.3.0/pca_sphere_projection/gene_geometry.py +370 -0
- sphere_pca-0.3.0/pca_sphere_projection/io.py +564 -0
- sphere_pca-0.3.0/pca_sphere_projection/known_regulators.py +116 -0
- sphere_pca-0.3.0/pca_sphere_projection/pc_robustness.py +115 -0
- sphere_pca-0.3.0/pca_sphere_projection/perturbation.py +458 -0
- sphere_pca-0.3.0/pca_sphere_projection/preprocessing.py +252 -0
- sphere_pca-0.3.0/pca_sphere_projection/robustness.py +260 -0
- sphere_pca-0.3.0/pca_sphere_projection/sphere_stats.py +312 -0
- sphere_pca-0.3.0/pca_sphere_projection/stripe.py +144 -0
- sphere_pca-0.3.0/pca_sphere_projection/topology.py +223 -0
- sphere_pca-0.3.0/pyproject.toml +109 -0
- sphere_pca-0.3.0/requirements-manuscript.txt +13 -0
- sphere_pca-0.3.0/scripts/manuscript/README.md +53 -0
- sphere_pca-0.3.0/scripts/manuscript/make_manuscript_figures.py +125 -0
- sphere_pca-0.3.0/scripts/manuscript/make_supplement_figures.py +398 -0
- sphere_pca-0.3.0/scripts/manuscript/smoke_test_celegan.py +40 -0
- sphere_pca-0.3.0/setup.cfg +4 -0
- sphere_pca-0.3.0/sphere_pca/__init__.py +14 -0
- sphere_pca-0.3.0/sphere_pca/cli.py +51 -0
- sphere_pca-0.3.0/sphere_pca/core.py +326 -0
- sphere_pca-0.3.0/sphere_pca/plotting.py +95 -0
- sphere_pca-0.3.0/sphere_pca/result.py +89 -0
- sphere_pca-0.3.0/sphere_pca.egg-info/PKG-INFO +216 -0
- sphere_pca-0.3.0/sphere_pca.egg-info/SOURCES.txt +70 -0
- sphere_pca-0.3.0/sphere_pca.egg-info/dependency_links.txt +1 -0
- sphere_pca-0.3.0/sphere_pca.egg-info/entry_points.txt +2 -0
- sphere_pca-0.3.0/sphere_pca.egg-info/requires.txt +54 -0
- sphere_pca-0.3.0/sphere_pca.egg-info/top_level.txt +2 -0
- sphere_pca-0.3.0/tests/test_comparison.py +53 -0
- sphere_pca-0.3.0/tests/test_perturbation.py +208 -0
- sphere_pca-0.3.0/tests/test_public_api.py +269 -0
- sphere_pca-0.3.0/tests/test_robustness.py +72 -0
- sphere_pca-0.3.0/tests/test_sphere_stats.py +73 -0
- 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"
|
sphere_pca-0.3.0/LICENSE
ADDED
|
@@ -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,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).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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}")
|