codeconv 1.0.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.
- codeconv-1.0.0/CHANGELOG.md +49 -0
- codeconv-1.0.0/LICENSE +21 -0
- codeconv-1.0.0/MANIFEST.in +4 -0
- codeconv-1.0.0/PKG-INFO +220 -0
- codeconv-1.0.0/README.md +175 -0
- codeconv-1.0.0/codeconv.egg-info/PKG-INFO +220 -0
- codeconv-1.0.0/codeconv.egg-info/SOURCES.txt +12 -0
- codeconv-1.0.0/codeconv.egg-info/dependency_links.txt +1 -0
- codeconv-1.0.0/codeconv.egg-info/requires.txt +18 -0
- codeconv-1.0.0/codeconv.egg-info/top_level.txt +1 -0
- codeconv-1.0.0/codeconv.py +3277 -0
- codeconv-1.0.0/codeconv_config.json +73 -0
- codeconv-1.0.0/pyproject.toml +78 -0
- codeconv-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
First public release. Accompanies Perik-Zavodskaia et al., *iScience* **29**, 116824 (2026), and the
|
|
6
|
+
STAR Protocols article describing its use end to end.
|
|
7
|
+
|
|
8
|
+
### What it does
|
|
9
|
+
|
|
10
|
+
Reconstructs discrete, individually placed cells carrying absolute whole-transcriptome counts from
|
|
11
|
+
spot-based spatial transcriptomics, with no external single-cell reference, and writes them in the
|
|
12
|
+
10x SpaceRanger layout so the result loads directly into Seurat, scanpy and CellChat.
|
|
13
|
+
|
|
14
|
+
### Notable behaviour
|
|
15
|
+
|
|
16
|
+
- **Automated topic-number selection.** `recommend_k()` offers five rules; the default keeps the K
|
|
17
|
+
values whose held-out perplexity falls in the best 5% of the sweep by rank and returns the largest
|
|
18
|
+
of them. `step5_ksweep()` prints the recommendation, draws it and the low-perplexity band on the
|
|
19
|
+
K-sweep figure, accepts a manual `n_topics=` override, and warns when the recommendation lands on
|
|
20
|
+
`max_k` — which means the sweep was too narrow to bound K from above. The choice is published as
|
|
21
|
+
`codeconv.recommended_K`, and on the result object as `.k`, with `.summary()` returning the whole
|
|
22
|
+
sweep as a DataFrame.
|
|
23
|
+
- **Built-in species profiles.** Human and mouse live in the module as `DEFAULT_CONFIG`, so a fresh
|
|
24
|
+
install runs with nothing else to download. An external `codeconv_config.json` takes precedence
|
|
25
|
+
when one is passed; `write_default_config()` dumps the built-ins for editing.
|
|
26
|
+
- **Platform detection.** `step1_acquisition_and_anchoring()` identifies the input layout and reports
|
|
27
|
+
it. 10x Visium and DBiT-seq are read natively, the DBiT-seq channel width being taken from the
|
|
28
|
+
sample name when it carries one. `PLATFORM_PROFILES` holds the geometry, `SliceData.platform`
|
|
29
|
+
records what was used, and `platform=` overrides detection. Capture units at or below the size of
|
|
30
|
+
one cell are deliberately out of scope: there is nothing to deconvolve, and segmentation rather
|
|
31
|
+
than a mixture model is the appropriate step.
|
|
32
|
+
- **CellChat hand-off.** `cellchat_spatial_factors()` derives the pixel-to-micron ratio and tolerance
|
|
33
|
+
for a spatial CellChat analysis and reports how many within-spot cell pairs a given interaction
|
|
34
|
+
range captures — the check that communication inference is invariant to the sub-spot cell
|
|
35
|
+
placement. `step9_export_results()` records all of it in `run_summary.json`, so the downstream R
|
|
36
|
+
session reads one file instead of re-deriving unit conversions. The ratio is calibrated from the
|
|
37
|
+
array's documented spot pitch divided by the pitch measured on the slide, because neither the 55 µm
|
|
38
|
+
physical spot diameter nor CellChat's 65 µm convention reproduces the real geometry.
|
|
39
|
+
- **Streaming reader with a parse cache.** A multi-gigabyte dense count table is parsed row by row
|
|
40
|
+
into a sparse matrix and cached beside the source, so later loads take under a second.
|
|
41
|
+
- **Placeholder histology.** `make_dummy_tissue_image()` writes a black canvas with matching scale
|
|
42
|
+
factors for platforms that ship no registered image, so the export still loads in Seurat.
|
|
43
|
+
|
|
44
|
+
### Note for users of the pre-release script
|
|
45
|
+
|
|
46
|
+
The first argument of `step1_acquisition_and_anchoring()` is now `spatial_path`, since the pipeline
|
|
47
|
+
is no longer Visium-only. The old `visium_path=` keyword still works and raises a
|
|
48
|
+
`DeprecationWarning`. `config_path` now defaults to `None` and `species` to `"hs"` on every step that
|
|
49
|
+
takes them.
|
codeconv-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Roman Perik-Zavodskii
|
|
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.
|
codeconv-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codeconv
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Reference-free single-cell-resolution deconvolution of spot-based spatial transcriptomics
|
|
5
|
+
Author: Roman Perik-Zavodskii, Saleh Alrhmoun
|
|
6
|
+
Author-email: Olga Perik-Zavodskaia <perik.zavodskaia@gmail.com>, Sergey Sennikov <sennikov@niikim.ru>
|
|
7
|
+
Maintainer-email: Olga Perik-Zavodskaia <perik.zavodskaia@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/Perik-Zavodskii/CoexpressDeconvolve
|
|
10
|
+
Project-URL: Repository, https://github.com/Perik-Zavodskii/CoexpressDeconvolve
|
|
11
|
+
Project-URL: Publication, https://doi.org/10.1016/j.isci.2026.116824
|
|
12
|
+
Project-URL: Archive, https://doi.org/10.5281/zenodo.21860849
|
|
13
|
+
Project-URL: Issues, https://github.com/Perik-Zavodskii/CoexpressDeconvolve/issues
|
|
14
|
+
Keywords: spatial transcriptomics,deconvolution,single-cell,Visium,DBiT-seq,topic model,bioinformatics
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: numpy>=1.21
|
|
29
|
+
Requires-Dist: pandas>=1.3
|
|
30
|
+
Requires-Dist: scipy>=1.7
|
|
31
|
+
Requires-Dist: scikit-learn>=1.0
|
|
32
|
+
Requires-Dist: matplotlib>=3.5
|
|
33
|
+
Requires-Dist: seaborn>=0.11
|
|
34
|
+
Requires-Dist: h5py>=3.1
|
|
35
|
+
Requires-Dist: tqdm>=4.62
|
|
36
|
+
Requires-Dist: umap-learn>=0.5.3
|
|
37
|
+
Provides-Extra: notebook
|
|
38
|
+
Requires-Dist: jupyter>=1.0; extra == "notebook"
|
|
39
|
+
Requires-Dist: ipywidgets>=8.0; extra == "notebook"
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
42
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
43
|
+
Requires-Dist: twine>=4.0; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# CoexpressDeconvolve
|
|
47
|
+
|
|
48
|
+
Spot-based spatial transcriptomics measures whole transcriptomes in place, but every spot pools
|
|
49
|
+
several cells, so a spot profile is a mixture rather than a cell. CoexpressDeconvolve resolves that
|
|
50
|
+
mixture into single-cell-like profiles, without needing a matched single-cell dataset to learn from.
|
|
51
|
+
|
|
52
|
+
The problem it addresses is that the usual route out of the mixture is a reference atlas, and a
|
|
53
|
+
reference is often unavailable, mismatched to the tissue, or missing the very populations that were
|
|
54
|
+
lost during dissociation. CoexpressDeconvolve works from the slide alone.
|
|
55
|
+
|
|
56
|
+
Three quantities carry the method. How many cells a spot captured is estimated from the
|
|
57
|
+
housekeeping-gene signal together with the total UMI count. Which expression programmes are present
|
|
58
|
+
is learned de novo from gene co-expression across the whole slide. How the spot's transcripts belong
|
|
59
|
+
to those cells follows from distributing its UMIs among them, conserving the per-spot total exactly:
|
|
60
|
+
the reconstructed cells of a spot add back up to the transcriptome measured there.
|
|
61
|
+
|
|
62
|
+
Each cell comes out with a whole transcriptome, absolute counts and a coordinate inside its parent
|
|
63
|
+
spot, written in the standard 10x SpaceRanger layout, so the result opens in Seurat or scanpy like
|
|
64
|
+
any other single-cell object.
|
|
65
|
+
|
|
66
|
+
Method and benchmarks: Perik-Zavodskaia, O., Perik-Zavodskii, R., Alrhmoun, S. & Sennikov, S.
|
|
67
|
+
*iScience* **29**, 116824 (2026). https://doi.org/10.1016/j.isci.2026.116824
|
|
68
|
+
|
|
69
|
+
## Supported platforms
|
|
70
|
+
|
|
71
|
+
The platform is detected from the input layout and announced at load. Everything downstream is
|
|
72
|
+
platform-agnostic: Step 1 normalizes every input to counts, coordinates, a capture footprint and a
|
|
73
|
+
pitch.
|
|
74
|
+
|
|
75
|
+
| Platform | Capture unit | Pitch | Layout expected | Histology |
|
|
76
|
+
|---|---|---|---|---|
|
|
77
|
+
| **10x Visium** (any capture area) | 55 µm | 100 µm | SpaceRanger: `filtered_feature_bc_matrix.h5` (or the `.mtx` folder) + `spatial/` | yes |
|
|
78
|
+
| **DBiT-seq** | 10 / 25 / 50 µm | 2 × channel width | one count table on the 50 × 50 grid, labels `AxB` | no |
|
|
79
|
+
|
|
80
|
+
The DBiT-seq channel width is read from the sample name when it carries one (`GSM4189611_50t` -> 50 µm),
|
|
81
|
+
and the pitch is twice the width. Platforms that ship no tissue image get a black placeholder canvas
|
|
82
|
+
sized to the capture grid, with synthesized scale factors, so `Seurat::Load10X_Spatial()` and every
|
|
83
|
+
spatial plot still work.
|
|
84
|
+
|
|
85
|
+
Capture units at or below the size of one cell are out of scope: there is nothing to deconvolve, and
|
|
86
|
+
segmentation rather than a mixture model is the right step there.
|
|
87
|
+
|
|
88
|
+
## Installation
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install codeconv
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Human (`hs`) and mouse (`mm`) species profiles are built into the module, so nothing else has to be
|
|
95
|
+
downloaded to get started.
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import codeconv
|
|
101
|
+
codeconv.set_seed(42)
|
|
102
|
+
|
|
103
|
+
spatial_path = "./Glioblastoma"
|
|
104
|
+
output_folder = "."
|
|
105
|
+
species = "hs" # "hs" | "mm" | anything in your config
|
|
106
|
+
|
|
107
|
+
slices = codeconv.step1_acquisition_and_anchoring(spatial_path)
|
|
108
|
+
slices = codeconv.step2_estimate_cell_density(slices, species=species,
|
|
109
|
+
min_umi=900, anchor_mean_factor=0.7)
|
|
110
|
+
odg_pack = codeconv.step3_feature_selection(slices, species=species, n_odg=3000)
|
|
111
|
+
manifold = codeconv.step4_gene_manifold(slices, odg_pack, n_components=30)
|
|
112
|
+
|
|
113
|
+
ksweep = codeconv.step5_ksweep(odg_pack, min_k=3, max_k=15)
|
|
114
|
+
print(ksweep.summary()) # per-K perplexity, rare topics, which K are in the low band
|
|
115
|
+
|
|
116
|
+
model = codeconv.step6_final_deconvolution(slices, odg_pack, manifold,
|
|
117
|
+
n_topics=codeconv.recommended_K, # or your own K
|
|
118
|
+
k_neighbors=3)
|
|
119
|
+
cells = codeconv.step7_sampling_engine(slices, model, species=species)
|
|
120
|
+
codeconv.step8_geometry_and_placement(cells, slices)
|
|
121
|
+
codeconv.step9_export_results(cells, slices, output_folder, interaction_range_um=50.0)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Several slices at once
|
|
125
|
+
|
|
126
|
+
Pass a dict instead of a string. Topics are aligned across slices, so the reconstructed cell types
|
|
127
|
+
are comparable between samples:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
spatial_path = {"sample_A": "./A", "sample_B": "./B"}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Per-slice parameters (`min_umi`, `anchor_mean_factor`, `low_slice_quality`) take either a scalar,
|
|
134
|
+
broadcast to every slice, or a dict keyed by slice name:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
slices = codeconv.step2_estimate_cell_density(
|
|
138
|
+
slices, species="hs",
|
|
139
|
+
min_umi={"sample_A": 900, "sample_B": 1200},
|
|
140
|
+
anchor_mean_factor=0.7,
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
A list of paths also works, with slice names taken from the folder names.
|
|
145
|
+
|
|
146
|
+
### Choosing K automatically
|
|
147
|
+
|
|
148
|
+
The number of topics used to be a visual judgement call. It no longer is:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
ksweep = codeconv.step5_ksweep(odg_pack, min_k=3, max_k=15) # prints the recommendation
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The default rule keeps every K whose held-out perplexity lands in the best 5% of the sweep by rank
|
|
155
|
+
and returns the **largest** of them, which is the highest K still in the low-perplexity regime. That
|
|
156
|
+
is deliberate: because CoexpressDeconvolve returns individual cells rather than fractions, a rare
|
|
157
|
+
topic is a minor cell population worth recovering, not evidence of over-splitting. Alternatives:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
codeconv.recommend_k(ksweep.perplexity['Glioblastoma'], rule="relative_tolerance", tol=0.01)
|
|
161
|
+
codeconv.recommend_k(ksweep.perplexity['Glioblastoma'], rule="lowest_perplexity")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A recommendation landing on `max_k` means perplexity had not turned back up yet and the sweep is too
|
|
165
|
+
narrow; you get a warning. Pass `n_topics=` to record a manual choice, and it is drawn on the plot
|
|
166
|
+
next to the automatic one.
|
|
167
|
+
|
|
168
|
+
### The 9 steps
|
|
169
|
+
|
|
170
|
+
1. **Acquisition** - detect the platform, load counts and coordinates, per-spot UMI QC.
|
|
171
|
+
2. **Density** - cells per spot from a hybrid housekeeping/UMI calibration. `low_slice_quality=True`
|
|
172
|
+
enforces a floor of one cell on every spot passing the UMI gate.
|
|
173
|
+
3. **Feature selection** - noise-gene regex, presence filter, overdispersed genes from the
|
|
174
|
+
mean-variance trend.
|
|
175
|
+
4. **Manifold** - joint gene co-expression topology via ICA + UMAP.
|
|
176
|
+
5. **K-sweep** - held-out perplexity across K, rare-topic counts, automatic recommendation.
|
|
177
|
+
6. **Deconvolution** - per-slice LDA, Hungarian topic alignment across slices, mean-consensus beta,
|
|
178
|
+
per-slice theta refit against the frozen consensus, projection onto each slice's full gene list.
|
|
179
|
+
7. **Sampling** - discrete cells drawn per spot; per-spot UMI totals are conserved exactly.
|
|
180
|
+
8. **Placement** - cells positioned inside their parent spot footprint by Vogel packing.
|
|
181
|
+
9. **Export** - 10x layout under `output_folder/slice_<name>/deconvolved/`, one folder per slice.
|
|
182
|
+
|
|
183
|
+
## Downstream analysis
|
|
184
|
+
|
|
185
|
+
Each slice exports a `filtered_feature_bc_matrix.h5` and a `spatial/` folder. From there the
|
|
186
|
+
reconstruction is an ordinary single-cell spatial object and goes wherever such objects go:
|
|
187
|
+
|
|
188
|
+
```r
|
|
189
|
+
library(Seurat)
|
|
190
|
+
seurat_obj <- Load10X_Spatial("./slice_Glioblastoma/deconvolved")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
import scanpy as sc
|
|
195
|
+
adata = sc.read_visium("./slice_Glioblastoma/deconvolved")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Clustering, annotation, trajectory inference, cell-cell communication - anything that takes a
|
|
199
|
+
single-cell object works, with no adapters. `Seurat Spatial.ipynb` in the repository is a worked
|
|
200
|
+
example. Topics are aligned across slices, so cluster comparison between samples is meaningful.
|
|
201
|
+
|
|
202
|
+
## Citation
|
|
203
|
+
|
|
204
|
+
```bibtex
|
|
205
|
+
@article{PerikZavodskaia2026CoexpressDeconvolve,
|
|
206
|
+
title = {CoexpressDeconvolve enables reference-free single-cell-resolution
|
|
207
|
+
deconvolution from spot-based spatial transcriptomics},
|
|
208
|
+
author = {Perik-Zavodskaia, Olga and Perik-Zavodskii, Roman and
|
|
209
|
+
Alrhmoun, Saleh and Sennikov, Sergey},
|
|
210
|
+
journal = {iScience},
|
|
211
|
+
volume = {29},
|
|
212
|
+
pages = {116824},
|
|
213
|
+
year = {2026},
|
|
214
|
+
doi = {10.1016/j.isci.2026.116824}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT, see `LICENSE`.
|
codeconv-1.0.0/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# CoexpressDeconvolve
|
|
2
|
+
|
|
3
|
+
Spot-based spatial transcriptomics measures whole transcriptomes in place, but every spot pools
|
|
4
|
+
several cells, so a spot profile is a mixture rather than a cell. CoexpressDeconvolve resolves that
|
|
5
|
+
mixture into single-cell-like profiles, without needing a matched single-cell dataset to learn from.
|
|
6
|
+
|
|
7
|
+
The problem it addresses is that the usual route out of the mixture is a reference atlas, and a
|
|
8
|
+
reference is often unavailable, mismatched to the tissue, or missing the very populations that were
|
|
9
|
+
lost during dissociation. CoexpressDeconvolve works from the slide alone.
|
|
10
|
+
|
|
11
|
+
Three quantities carry the method. How many cells a spot captured is estimated from the
|
|
12
|
+
housekeeping-gene signal together with the total UMI count. Which expression programmes are present
|
|
13
|
+
is learned de novo from gene co-expression across the whole slide. How the spot's transcripts belong
|
|
14
|
+
to those cells follows from distributing its UMIs among them, conserving the per-spot total exactly:
|
|
15
|
+
the reconstructed cells of a spot add back up to the transcriptome measured there.
|
|
16
|
+
|
|
17
|
+
Each cell comes out with a whole transcriptome, absolute counts and a coordinate inside its parent
|
|
18
|
+
spot, written in the standard 10x SpaceRanger layout, so the result opens in Seurat or scanpy like
|
|
19
|
+
any other single-cell object.
|
|
20
|
+
|
|
21
|
+
Method and benchmarks: Perik-Zavodskaia, O., Perik-Zavodskii, R., Alrhmoun, S. & Sennikov, S.
|
|
22
|
+
*iScience* **29**, 116824 (2026). https://doi.org/10.1016/j.isci.2026.116824
|
|
23
|
+
|
|
24
|
+
## Supported platforms
|
|
25
|
+
|
|
26
|
+
The platform is detected from the input layout and announced at load. Everything downstream is
|
|
27
|
+
platform-agnostic: Step 1 normalizes every input to counts, coordinates, a capture footprint and a
|
|
28
|
+
pitch.
|
|
29
|
+
|
|
30
|
+
| Platform | Capture unit | Pitch | Layout expected | Histology |
|
|
31
|
+
|---|---|---|---|---|
|
|
32
|
+
| **10x Visium** (any capture area) | 55 µm | 100 µm | SpaceRanger: `filtered_feature_bc_matrix.h5` (or the `.mtx` folder) + `spatial/` | yes |
|
|
33
|
+
| **DBiT-seq** | 10 / 25 / 50 µm | 2 × channel width | one count table on the 50 × 50 grid, labels `AxB` | no |
|
|
34
|
+
|
|
35
|
+
The DBiT-seq channel width is read from the sample name when it carries one (`GSM4189611_50t` -> 50 µm),
|
|
36
|
+
and the pitch is twice the width. Platforms that ship no tissue image get a black placeholder canvas
|
|
37
|
+
sized to the capture grid, with synthesized scale factors, so `Seurat::Load10X_Spatial()` and every
|
|
38
|
+
spatial plot still work.
|
|
39
|
+
|
|
40
|
+
Capture units at or below the size of one cell are out of scope: there is nothing to deconvolve, and
|
|
41
|
+
segmentation rather than a mixture model is the right step there.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install codeconv
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Human (`hs`) and mouse (`mm`) species profiles are built into the module, so nothing else has to be
|
|
50
|
+
downloaded to get started.
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import codeconv
|
|
56
|
+
codeconv.set_seed(42)
|
|
57
|
+
|
|
58
|
+
spatial_path = "./Glioblastoma"
|
|
59
|
+
output_folder = "."
|
|
60
|
+
species = "hs" # "hs" | "mm" | anything in your config
|
|
61
|
+
|
|
62
|
+
slices = codeconv.step1_acquisition_and_anchoring(spatial_path)
|
|
63
|
+
slices = codeconv.step2_estimate_cell_density(slices, species=species,
|
|
64
|
+
min_umi=900, anchor_mean_factor=0.7)
|
|
65
|
+
odg_pack = codeconv.step3_feature_selection(slices, species=species, n_odg=3000)
|
|
66
|
+
manifold = codeconv.step4_gene_manifold(slices, odg_pack, n_components=30)
|
|
67
|
+
|
|
68
|
+
ksweep = codeconv.step5_ksweep(odg_pack, min_k=3, max_k=15)
|
|
69
|
+
print(ksweep.summary()) # per-K perplexity, rare topics, which K are in the low band
|
|
70
|
+
|
|
71
|
+
model = codeconv.step6_final_deconvolution(slices, odg_pack, manifold,
|
|
72
|
+
n_topics=codeconv.recommended_K, # or your own K
|
|
73
|
+
k_neighbors=3)
|
|
74
|
+
cells = codeconv.step7_sampling_engine(slices, model, species=species)
|
|
75
|
+
codeconv.step8_geometry_and_placement(cells, slices)
|
|
76
|
+
codeconv.step9_export_results(cells, slices, output_folder, interaction_range_um=50.0)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Several slices at once
|
|
80
|
+
|
|
81
|
+
Pass a dict instead of a string. Topics are aligned across slices, so the reconstructed cell types
|
|
82
|
+
are comparable between samples:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
spatial_path = {"sample_A": "./A", "sample_B": "./B"}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Per-slice parameters (`min_umi`, `anchor_mean_factor`, `low_slice_quality`) take either a scalar,
|
|
89
|
+
broadcast to every slice, or a dict keyed by slice name:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
slices = codeconv.step2_estimate_cell_density(
|
|
93
|
+
slices, species="hs",
|
|
94
|
+
min_umi={"sample_A": 900, "sample_B": 1200},
|
|
95
|
+
anchor_mean_factor=0.7,
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A list of paths also works, with slice names taken from the folder names.
|
|
100
|
+
|
|
101
|
+
### Choosing K automatically
|
|
102
|
+
|
|
103
|
+
The number of topics used to be a visual judgement call. It no longer is:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
ksweep = codeconv.step5_ksweep(odg_pack, min_k=3, max_k=15) # prints the recommendation
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The default rule keeps every K whose held-out perplexity lands in the best 5% of the sweep by rank
|
|
110
|
+
and returns the **largest** of them, which is the highest K still in the low-perplexity regime. That
|
|
111
|
+
is deliberate: because CoexpressDeconvolve returns individual cells rather than fractions, a rare
|
|
112
|
+
topic is a minor cell population worth recovering, not evidence of over-splitting. Alternatives:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
codeconv.recommend_k(ksweep.perplexity['Glioblastoma'], rule="relative_tolerance", tol=0.01)
|
|
116
|
+
codeconv.recommend_k(ksweep.perplexity['Glioblastoma'], rule="lowest_perplexity")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
A recommendation landing on `max_k` means perplexity had not turned back up yet and the sweep is too
|
|
120
|
+
narrow; you get a warning. Pass `n_topics=` to record a manual choice, and it is drawn on the plot
|
|
121
|
+
next to the automatic one.
|
|
122
|
+
|
|
123
|
+
### The 9 steps
|
|
124
|
+
|
|
125
|
+
1. **Acquisition** - detect the platform, load counts and coordinates, per-spot UMI QC.
|
|
126
|
+
2. **Density** - cells per spot from a hybrid housekeeping/UMI calibration. `low_slice_quality=True`
|
|
127
|
+
enforces a floor of one cell on every spot passing the UMI gate.
|
|
128
|
+
3. **Feature selection** - noise-gene regex, presence filter, overdispersed genes from the
|
|
129
|
+
mean-variance trend.
|
|
130
|
+
4. **Manifold** - joint gene co-expression topology via ICA + UMAP.
|
|
131
|
+
5. **K-sweep** - held-out perplexity across K, rare-topic counts, automatic recommendation.
|
|
132
|
+
6. **Deconvolution** - per-slice LDA, Hungarian topic alignment across slices, mean-consensus beta,
|
|
133
|
+
per-slice theta refit against the frozen consensus, projection onto each slice's full gene list.
|
|
134
|
+
7. **Sampling** - discrete cells drawn per spot; per-spot UMI totals are conserved exactly.
|
|
135
|
+
8. **Placement** - cells positioned inside their parent spot footprint by Vogel packing.
|
|
136
|
+
9. **Export** - 10x layout under `output_folder/slice_<name>/deconvolved/`, one folder per slice.
|
|
137
|
+
|
|
138
|
+
## Downstream analysis
|
|
139
|
+
|
|
140
|
+
Each slice exports a `filtered_feature_bc_matrix.h5` and a `spatial/` folder. From there the
|
|
141
|
+
reconstruction is an ordinary single-cell spatial object and goes wherever such objects go:
|
|
142
|
+
|
|
143
|
+
```r
|
|
144
|
+
library(Seurat)
|
|
145
|
+
seurat_obj <- Load10X_Spatial("./slice_Glioblastoma/deconvolved")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
import scanpy as sc
|
|
150
|
+
adata = sc.read_visium("./slice_Glioblastoma/deconvolved")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Clustering, annotation, trajectory inference, cell-cell communication - anything that takes a
|
|
154
|
+
single-cell object works, with no adapters. `Seurat Spatial.ipynb` in the repository is a worked
|
|
155
|
+
example. Topics are aligned across slices, so cluster comparison between samples is meaningful.
|
|
156
|
+
|
|
157
|
+
## Citation
|
|
158
|
+
|
|
159
|
+
```bibtex
|
|
160
|
+
@article{PerikZavodskaia2026CoexpressDeconvolve,
|
|
161
|
+
title = {CoexpressDeconvolve enables reference-free single-cell-resolution
|
|
162
|
+
deconvolution from spot-based spatial transcriptomics},
|
|
163
|
+
author = {Perik-Zavodskaia, Olga and Perik-Zavodskii, Roman and
|
|
164
|
+
Alrhmoun, Saleh and Sennikov, Sergey},
|
|
165
|
+
journal = {iScience},
|
|
166
|
+
volume = {29},
|
|
167
|
+
pages = {116824},
|
|
168
|
+
year = {2026},
|
|
169
|
+
doi = {10.1016/j.isci.2026.116824}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT, see `LICENSE`.
|