cytocommunity2 0.1.1__tar.gz → 0.1.2__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.
- cytocommunity2-0.1.2/LICENSE +21 -0
- cytocommunity2-0.1.2/PKG-INFO +167 -0
- cytocommunity2-0.1.2/README.md +146 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/pyproject.toml +5 -6
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/cca.py +6 -13
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/api.py +39 -42
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/dataset.py +3 -4
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/selection.py +106 -107
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/training.py +17 -8
- cytocommunity2-0.1.2/src/cytocommunity2.egg-info/PKG-INFO +167 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/SOURCES.txt +1 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/requires.txt +0 -1
- cytocommunity2-0.1.1/PKG-INFO +0 -27
- cytocommunity2-0.1.1/README.md +0 -7
- cytocommunity2-0.1.1/src/cytocommunity2.egg-info/PKG-INFO +0 -27
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/setup.cfg +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/__init__.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/colors.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/config.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/__init__.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/coherence.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/__init__.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/between.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/utils.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/within.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/composition.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/moran.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/__init__.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/cn_selection.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/coherence.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/common.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/communication.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/composition.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/dominant.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/dotplots.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/recurrence.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/runtime.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/ensemble.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/__init__.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/learning/model.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/paths.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/visualization/__init__.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/visualization/spatial.py +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/dependency_links.txt +0 -0
- {cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 huBioinfo
|
|
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,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cytocommunity2
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Weakly supervised cellular-neighborhood learning for spatial omics
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: matplotlib>=3.6
|
|
9
|
+
Requires-Dist: numpy<2,>=1.23
|
|
10
|
+
Requires-Dist: pandas<3,>=1.5
|
|
11
|
+
Requires-Dist: sci-palettes
|
|
12
|
+
Requires-Dist: scikit-learn>=1.1
|
|
13
|
+
Requires-Dist: scipy>=1.9
|
|
14
|
+
Requires-Dist: seaborn>=0.12
|
|
15
|
+
Requires-Dist: torch-geometric>=2.0
|
|
16
|
+
Provides-Extra: downstream
|
|
17
|
+
Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
|
|
18
|
+
Requires-Dist: jupyter; extra == "downstream"
|
|
19
|
+
Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# CytoCommunity2
|
|
23
|
+
|
|
24
|
+
## Contents
|
|
25
|
+
|
|
26
|
+
- [Overview](#overview)
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Maintainers](#maintainers)
|
|
30
|
+
- [Citation](#citation)
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
|
|
34
|
+
To enhance CytoCommunity (https://github.com/huBioinfo/CytoCommunity), we
|
|
35
|
+
present CytoCommunity2, **a unified weakly-supervised framework** for
|
|
36
|
+
identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across
|
|
37
|
+
**large-scale spatial omics samples with single or multiple biological
|
|
38
|
+
conditions**.
|
|
39
|
+
|
|
40
|
+
Inspired by histopathology workflows, CytoCommunity2 first hierarchically
|
|
41
|
+
partitions the large single-cell spatial map into small patches, performs graph
|
|
42
|
+
construction and weakly supervised TCN learning for each patch, and finally
|
|
43
|
+
merges results through KNN-based TCN reassignment at segmentation boundaries to
|
|
44
|
+
ensure TCN spatial continuity. This strategy divides the original sample into
|
|
45
|
+
patches for TCN learning, achieving **memory efficiency (typical 24G graphics
|
|
46
|
+
memory is enough)** and also increased sample throughput. These optimizations
|
|
47
|
+
significantly **enhance the robustness of TCNs and cross-sample alignment
|
|
48
|
+
performance**.
|
|
49
|
+
|
|
50
|
+
Furthermore, to make CytoCommunity2 a unified framework that is also
|
|
51
|
+
**applicable for single-condition spatial omics datasets**, pseudo-samples with
|
|
52
|
+
artificial labels are generated, enabling automatic TCN alignment across real
|
|
53
|
+
samples via **contrastive learning**.
|
|
54
|
+
|
|
55
|
+
**_In summary, the advantages of CytoCommunity2 include:_**
|
|
56
|
+
|
|
57
|
+
**_(1) Using significantly less memory for large-scale spatial omics samples
|
|
58
|
+
with millions of cells._**
|
|
59
|
+
|
|
60
|
+
**_(2) A unified weakly-supervised model applicable for both multi-condition
|
|
61
|
+
and single-condition datasets._**
|
|
62
|
+
|
|
63
|
+
**_(3) High TCN alignment performance makes it well-suited for comparative
|
|
64
|
+
analysis in large cohort studies._**
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
### Hardware requirement
|
|
69
|
+
|
|
70
|
+
Graphics memory: 24G
|
|
71
|
+
|
|
72
|
+
Storage: 10GB or more
|
|
73
|
+
|
|
74
|
+
### Install CytoCommunity2
|
|
75
|
+
|
|
76
|
+
CytoCommunity2 requires Python 3.10 or newer. We recommend creating and
|
|
77
|
+
activating a Conda environment first:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
conda create -n cytocommunity2 python=3.10
|
|
81
|
+
conda activate cytocommunity2
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Install PyTorch for your compute platform before installing CytoCommunity2.
|
|
85
|
+
For an NVIDIA GPU on Windows or Linux, first install the appropriate
|
|
86
|
+
CUDA-enabled PyTorch build from the
|
|
87
|
+
[official PyTorch installation page](https://pytorch.org/get-started/locally/).
|
|
88
|
+
|
|
89
|
+
Install the package:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install cytocommunity2
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
To run the downstream analyses, install the
|
|
96
|
+
optional downstream dependencies with:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install "cytocommunity2[downstream]"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This extra includes `esda`, `libpysal`, and Jupyter; they are not installed
|
|
103
|
+
by the basic command above.
|
|
104
|
+
|
|
105
|
+
## Usage
|
|
106
|
+
|
|
107
|
+
### 1. Prepare input data
|
|
108
|
+
|
|
109
|
+
The input data to CytoCommunity2 includes four types of files :
|
|
110
|
+
|
|
111
|
+
(1) An image (sample) name list file, named as "ImageNameList.txt".
|
|
112
|
+
|
|
113
|
+
(2) A cell type label file for each image (sample), named as "[image
|
|
114
|
+
name]_CellTypeLabel.txt". Note that [image_name] should be consistent with your
|
|
115
|
+
customized image names listed in the "ImageNameList.txt". This file lists cell
|
|
116
|
+
type names of all cells in an image (sample).
|
|
117
|
+
|
|
118
|
+
(3) A cell spatial coordinate file for each image (sample), named as "[image
|
|
119
|
+
name]_Coordinates.txt". Note that [image_name] should be consistent with your
|
|
120
|
+
customized image names listed in the "ImageNameList.txt". This file lists cell
|
|
121
|
+
coordinates (tab-delimited x/y) of all cells in an image (sample). The cell
|
|
122
|
+
orders should be exactly the same with "[image name]_CellTypeLabel.txt".
|
|
123
|
+
|
|
124
|
+
(4) A graph label file for each image (sample), named as "[image
|
|
125
|
+
name]_GraphLabel.txt". For multi-condition datasets, this file must be provided
|
|
126
|
+
for every image (sample) and contain one integer indicating its biological
|
|
127
|
+
condition. For single-condition datasets, graph label
|
|
128
|
+
files can be omitted for all images (samples), and CytoCommunity2 will generate
|
|
129
|
+
pseudo-samples for weakly-supervised learning.
|
|
130
|
+
|
|
131
|
+
### 2. Configure CytoCommunity2
|
|
132
|
+
|
|
133
|
+
Before running CytoCommunity2, set `input_dir` and review all
|
|
134
|
+
model and downstream parameters in
|
|
135
|
+
[`hyperparameters.json`](https://github.com/LiukangWu/CytoCommunity2/blob/main/hyperparameters.json). The Python workflow will use this configuration file.
|
|
136
|
+
|
|
137
|
+
### 3. Run CytoCommunity2
|
|
138
|
+
|
|
139
|
+
The tutorial uses a TNBC dataset as a worked example and demonstrates the
|
|
140
|
+
entire CytoCommunity2 workflow, from model training and CN identification to
|
|
141
|
+
downstream analysis:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
jupyter notebook tutorial/TNBC_workflow.ipynb
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 4. Output files
|
|
148
|
+
|
|
149
|
+
The main output directories are:
|
|
150
|
+
|
|
151
|
+
- `CNLearning_Output/`: CN-number screening, trained models and CN labels.
|
|
152
|
+
- `CNVisualization_Output/`: spatial plots and result tables.
|
|
153
|
+
- `data/`: downstream analysis results and intermediate data tables.
|
|
154
|
+
- `plot/`: figures generated by downstream analyses.
|
|
155
|
+
|
|
156
|
+
## Maintainers
|
|
157
|
+
|
|
158
|
+
- Liukang Wu (yetong@stu.xidian.edu.cn)
|
|
159
|
+
- Yafei Xu (22031212416@stu.xidian.edu.cn)
|
|
160
|
+
- Yuxuan Hu (huyuxuan@xidian.edu.cn)
|
|
161
|
+
|
|
162
|
+
## Citation
|
|
163
|
+
|
|
164
|
+
Yuxuan Hu, Jiazhen Rong, Yafei Xu, Runzhi Xie, Jacqueline Peng, Lin Gao, and
|
|
165
|
+
Kai Tan. “Unsupervised and supervised discovery of tissue cellular
|
|
166
|
+
neighborhoods from cell phenotypes.” *Nature Methods* 21 (2024): 267–278.
|
|
167
|
+
https://doi.org/10.1038/s41592-023-02124-2
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# CytoCommunity2
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Installation](#installation)
|
|
7
|
+
- [Usage](#usage)
|
|
8
|
+
- [Maintainers](#maintainers)
|
|
9
|
+
- [Citation](#citation)
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
To enhance CytoCommunity (https://github.com/huBioinfo/CytoCommunity), we
|
|
14
|
+
present CytoCommunity2, **a unified weakly-supervised framework** for
|
|
15
|
+
identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across
|
|
16
|
+
**large-scale spatial omics samples with single or multiple biological
|
|
17
|
+
conditions**.
|
|
18
|
+
|
|
19
|
+
Inspired by histopathology workflows, CytoCommunity2 first hierarchically
|
|
20
|
+
partitions the large single-cell spatial map into small patches, performs graph
|
|
21
|
+
construction and weakly supervised TCN learning for each patch, and finally
|
|
22
|
+
merges results through KNN-based TCN reassignment at segmentation boundaries to
|
|
23
|
+
ensure TCN spatial continuity. This strategy divides the original sample into
|
|
24
|
+
patches for TCN learning, achieving **memory efficiency (typical 24G graphics
|
|
25
|
+
memory is enough)** and also increased sample throughput. These optimizations
|
|
26
|
+
significantly **enhance the robustness of TCNs and cross-sample alignment
|
|
27
|
+
performance**.
|
|
28
|
+
|
|
29
|
+
Furthermore, to make CytoCommunity2 a unified framework that is also
|
|
30
|
+
**applicable for single-condition spatial omics datasets**, pseudo-samples with
|
|
31
|
+
artificial labels are generated, enabling automatic TCN alignment across real
|
|
32
|
+
samples via **contrastive learning**.
|
|
33
|
+
|
|
34
|
+
**_In summary, the advantages of CytoCommunity2 include:_**
|
|
35
|
+
|
|
36
|
+
**_(1) Using significantly less memory for large-scale spatial omics samples
|
|
37
|
+
with millions of cells._**
|
|
38
|
+
|
|
39
|
+
**_(2) A unified weakly-supervised model applicable for both multi-condition
|
|
40
|
+
and single-condition datasets._**
|
|
41
|
+
|
|
42
|
+
**_(3) High TCN alignment performance makes it well-suited for comparative
|
|
43
|
+
analysis in large cohort studies._**
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### Hardware requirement
|
|
48
|
+
|
|
49
|
+
Graphics memory: 24G
|
|
50
|
+
|
|
51
|
+
Storage: 10GB or more
|
|
52
|
+
|
|
53
|
+
### Install CytoCommunity2
|
|
54
|
+
|
|
55
|
+
CytoCommunity2 requires Python 3.10 or newer. We recommend creating and
|
|
56
|
+
activating a Conda environment first:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
conda create -n cytocommunity2 python=3.10
|
|
60
|
+
conda activate cytocommunity2
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Install PyTorch for your compute platform before installing CytoCommunity2.
|
|
64
|
+
For an NVIDIA GPU on Windows or Linux, first install the appropriate
|
|
65
|
+
CUDA-enabled PyTorch build from the
|
|
66
|
+
[official PyTorch installation page](https://pytorch.org/get-started/locally/).
|
|
67
|
+
|
|
68
|
+
Install the package:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install cytocommunity2
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
To run the downstream analyses, install the
|
|
75
|
+
optional downstream dependencies with:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install "cytocommunity2[downstream]"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This extra includes `esda`, `libpysal`, and Jupyter; they are not installed
|
|
82
|
+
by the basic command above.
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
### 1. Prepare input data
|
|
87
|
+
|
|
88
|
+
The input data to CytoCommunity2 includes four types of files :
|
|
89
|
+
|
|
90
|
+
(1) An image (sample) name list file, named as "ImageNameList.txt".
|
|
91
|
+
|
|
92
|
+
(2) A cell type label file for each image (sample), named as "[image
|
|
93
|
+
name]_CellTypeLabel.txt". Note that [image_name] should be consistent with your
|
|
94
|
+
customized image names listed in the "ImageNameList.txt". This file lists cell
|
|
95
|
+
type names of all cells in an image (sample).
|
|
96
|
+
|
|
97
|
+
(3) A cell spatial coordinate file for each image (sample), named as "[image
|
|
98
|
+
name]_Coordinates.txt". Note that [image_name] should be consistent with your
|
|
99
|
+
customized image names listed in the "ImageNameList.txt". This file lists cell
|
|
100
|
+
coordinates (tab-delimited x/y) of all cells in an image (sample). The cell
|
|
101
|
+
orders should be exactly the same with "[image name]_CellTypeLabel.txt".
|
|
102
|
+
|
|
103
|
+
(4) A graph label file for each image (sample), named as "[image
|
|
104
|
+
name]_GraphLabel.txt". For multi-condition datasets, this file must be provided
|
|
105
|
+
for every image (sample) and contain one integer indicating its biological
|
|
106
|
+
condition. For single-condition datasets, graph label
|
|
107
|
+
files can be omitted for all images (samples), and CytoCommunity2 will generate
|
|
108
|
+
pseudo-samples for weakly-supervised learning.
|
|
109
|
+
|
|
110
|
+
### 2. Configure CytoCommunity2
|
|
111
|
+
|
|
112
|
+
Before running CytoCommunity2, set `input_dir` and review all
|
|
113
|
+
model and downstream parameters in
|
|
114
|
+
[`hyperparameters.json`](https://github.com/LiukangWu/CytoCommunity2/blob/main/hyperparameters.json). The Python workflow will use this configuration file.
|
|
115
|
+
|
|
116
|
+
### 3. Run CytoCommunity2
|
|
117
|
+
|
|
118
|
+
The tutorial uses a TNBC dataset as a worked example and demonstrates the
|
|
119
|
+
entire CytoCommunity2 workflow, from model training and CN identification to
|
|
120
|
+
downstream analysis:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
jupyter notebook tutorial/TNBC_workflow.ipynb
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 4. Output files
|
|
127
|
+
|
|
128
|
+
The main output directories are:
|
|
129
|
+
|
|
130
|
+
- `CNLearning_Output/`: CN-number screening, trained models and CN labels.
|
|
131
|
+
- `CNVisualization_Output/`: spatial plots and result tables.
|
|
132
|
+
- `data/`: downstream analysis results and intermediate data tables.
|
|
133
|
+
- `plot/`: figures generated by downstream analyses.
|
|
134
|
+
|
|
135
|
+
## Maintainers
|
|
136
|
+
|
|
137
|
+
- Liukang Wu (yetong@stu.xidian.edu.cn)
|
|
138
|
+
- Yafei Xu (22031212416@stu.xidian.edu.cn)
|
|
139
|
+
- Yuxuan Hu (huyuxuan@xidian.edu.cn)
|
|
140
|
+
|
|
141
|
+
## Citation
|
|
142
|
+
|
|
143
|
+
Yuxuan Hu, Jiazhen Rong, Yafei Xu, Runzhi Xie, Jacqueline Peng, Lin Gao, and
|
|
144
|
+
Kai Tan. “Unsupervised and supervised discovery of tissue cellular
|
|
145
|
+
neighborhoods from cell phenotypes.” *Nature Methods* 21 (2024): 267–278.
|
|
146
|
+
https://doi.org/10.1038/s41592-023-02124-2
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cytocommunity2"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Weakly supervised cellular-neighborhood learning for spatial omics"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
|
-
dependencies = [
|
|
11
|
+
dependencies = [
|
|
12
12
|
"matplotlib>=3.6",
|
|
13
13
|
"numpy>=1.23,<2",
|
|
14
14
|
"pandas>=1.5,<3",
|
|
@@ -16,7 +16,6 @@ dependencies = [
|
|
|
16
16
|
"scikit-learn>=1.1",
|
|
17
17
|
"scipy>=1.9",
|
|
18
18
|
"seaborn>=0.12",
|
|
19
|
-
"torch>=1.11",
|
|
20
19
|
"torch-geometric>=2.0",
|
|
21
20
|
]
|
|
22
21
|
|
|
@@ -27,6 +26,6 @@ downstream = [
|
|
|
27
26
|
"libpysal>=4.9,<4.10",
|
|
28
27
|
]
|
|
29
28
|
|
|
30
|
-
[tool.setuptools.packages.find]
|
|
31
|
-
where = ["src"]
|
|
32
|
-
include = ["cytocommunity2*"]
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["src"]
|
|
31
|
+
include = ["cytocommunity2*"]
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/cca.py
RENAMED
|
@@ -169,14 +169,7 @@ def run_cca_for_scores(
|
|
|
169
169
|
"Y_mat": y_matrix,
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
if condition is not None:
|
|
174
|
-
columns.append("Condition")
|
|
175
|
-
for component in range(communication_params["cca_canonical_components"]):
|
|
176
|
-
columns.extend(
|
|
177
|
-
[f"rho{component + 1}", f"pval{component + 1}", f"invalid_perm{component + 1}"]
|
|
178
|
-
)
|
|
179
|
-
results = pd.DataFrame(result_rows, columns=columns)
|
|
172
|
+
results = pd.DataFrame(result_rows)
|
|
180
173
|
if not results.empty:
|
|
181
174
|
results = results.sort_values(
|
|
182
175
|
"rho1", key=lambda values: values.abs(), ascending=False, na_position="last"
|
|
@@ -303,11 +296,11 @@ def run_cca_analysis(config, *, input_file=None, output_dir=None):
|
|
|
303
296
|
condition,
|
|
304
297
|
)
|
|
305
298
|
condition_results.append(result["results"])
|
|
306
|
-
by_condition = (
|
|
307
|
-
pd.concat(condition_results, ignore_index=True)
|
|
308
|
-
if condition_results
|
|
309
|
-
else pd.DataFrame(
|
|
310
|
-
)
|
|
299
|
+
by_condition = (
|
|
300
|
+
pd.concat(condition_results, ignore_index=True)
|
|
301
|
+
if condition_results
|
|
302
|
+
else pd.DataFrame()
|
|
303
|
+
)
|
|
311
304
|
if not by_condition.empty:
|
|
312
305
|
by_condition["Condition"] = pd.Categorical(
|
|
313
306
|
by_condition["Condition"], categories=conditions, ordered=True
|
|
@@ -8,12 +8,11 @@ import shutil
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
|
|
10
10
|
from ..config import CytoCommunityConfig
|
|
11
|
-
from ..ensemble import ensemble_cn
|
|
12
|
-
from ..paths import build_paths
|
|
11
|
+
from ..ensemble import ensemble_cn
|
|
13
12
|
from .dataset import SpatialGraphDataset
|
|
14
13
|
from .model import TrainingConfig
|
|
15
14
|
from .selection import evaluate_cn_num, select_cn_num
|
|
16
|
-
from .training import train_one_run
|
|
15
|
+
from .training import train_one_run
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
@dataclass(frozen=True)
|
|
@@ -34,9 +33,11 @@ class LearningResult:
|
|
|
34
33
|
def load_learning_result(
|
|
35
34
|
dataset: SpatialGraphDataset,
|
|
36
35
|
config: CytoCommunityConfig,
|
|
36
|
+
*,
|
|
37
|
+
output_dir,
|
|
37
38
|
) -> LearningResult:
|
|
38
39
|
"""Restore a :class:`LearningResult` from a completed learning run."""
|
|
39
|
-
output_dir =
|
|
40
|
+
output_dir = Path(output_dir).expanduser().resolve()
|
|
40
41
|
paths = {
|
|
41
42
|
"selected CN number": output_dir / "SelectedCNNum.txt",
|
|
42
43
|
"cell-type list": output_dir / "UniqueCellTypeList.txt",
|
|
@@ -85,11 +86,13 @@ def _training_config(config, num_cn, num_class):
|
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
def run_learning(
|
|
88
|
-
dataset: SpatialGraphDataset,
|
|
89
|
-
config: CytoCommunityConfig,
|
|
89
|
+
dataset: SpatialGraphDataset,
|
|
90
|
+
config: CytoCommunityConfig,
|
|
91
|
+
*,
|
|
92
|
+
output_dir,
|
|
90
93
|
) -> LearningResult:
|
|
91
|
-
"""Select the CN number, train final runs, and export consensus labels."""
|
|
92
|
-
output_dir =
|
|
94
|
+
"""Select the CN number, train final runs, and export consensus labels."""
|
|
95
|
+
output_dir = Path(output_dir).expanduser().resolve()
|
|
93
96
|
input_dir = config.input_dir
|
|
94
97
|
unique_celltypes_file = output_dir / "UniqueCellTypeList.txt"
|
|
95
98
|
selected_model_dir = output_dir / "SelectedModel"
|
|
@@ -147,44 +150,38 @@ def run_learning(
|
|
|
147
150
|
)
|
|
148
151
|
del hard_assignments
|
|
149
152
|
selection_results.append(result)
|
|
150
|
-
cn_pvalues = cn_pvalues[
|
|
151
|
-
[
|
|
152
|
-
"CN",
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"background_mean",
|
|
157
|
-
"delta_mean",
|
|
158
|
-
"KSStatistic",
|
|
159
|
-
"KSLocation",
|
|
160
|
-
"p_value_one_sided",
|
|
161
|
-
"p_adjusted_bh",
|
|
162
|
-
"RecurrenceScore",
|
|
153
|
+
cn_pvalues = cn_pvalues[
|
|
154
|
+
[
|
|
155
|
+
"CN",
|
|
156
|
+
"p_value_one_sided",
|
|
157
|
+
"p_adjusted_bh",
|
|
158
|
+
"RecurrenceScore",
|
|
163
159
|
"SignificantRecurrence",
|
|
164
160
|
]
|
|
165
161
|
].copy()
|
|
166
162
|
cn_pvalues.insert(0, "NumCN", num_cn)
|
|
167
|
-
cn_pvalue_tables.append(cn_pvalues)
|
|
168
|
-
print(
|
|
169
|
-
f"K={num_cn}:
|
|
170
|
-
f"significantFraction={result['SignificantFraction']:.6f}, "
|
|
171
|
-
f"
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
pd.
|
|
175
|
-
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
f"
|
|
187
|
-
|
|
163
|
+
cn_pvalue_tables.append(cn_pvalues)
|
|
164
|
+
print(
|
|
165
|
+
f"K={num_cn}: significantCN={result['SignificantCN']}, "
|
|
166
|
+
f"significantFraction={result['SignificantFraction']:.6f}, "
|
|
167
|
+
f"meanRS={result['MeanRS']:.6f}, score={result['Score']:.6f}"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
pd.DataFrame(selection_results).sort_values("NumCN").to_csv(
|
|
171
|
+
selection_summary_file, index=False
|
|
172
|
+
)
|
|
173
|
+
pd.concat(cn_pvalue_tables, ignore_index=True).to_csv(
|
|
174
|
+
cn_pvalues_file, index=False
|
|
175
|
+
)
|
|
176
|
+
selected = select_cn_num(selection_results)
|
|
177
|
+
selected_cn = int(selected["NumCN"])
|
|
178
|
+
(output_dir / "SelectedCNNum.txt").write_text(
|
|
179
|
+
f"{selected_cn}\n", encoding="utf-8"
|
|
180
|
+
)
|
|
181
|
+
print(
|
|
182
|
+
f"\nSelected K={selected_cn} with score={selected['Score']:.6f}. "
|
|
183
|
+
f"Training a fresh set of Runs 1-{config.main.final_num_runs}."
|
|
184
|
+
)
|
|
188
185
|
|
|
189
186
|
selected_config = _training_config(
|
|
190
187
|
config, num_cn=selected_cn, num_class=dataset.num_classes
|
|
@@ -64,10 +64,9 @@ def _build_knn_edge_index(coordinates, knn_k):
|
|
|
64
64
|
).contiguous()
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def build_spatial_graphs(
|
|
68
|
-
"""Read
|
|
69
|
-
input_folder =
|
|
70
|
-
knn_k = config.main.knn_k
|
|
67
|
+
def build_spatial_graphs(input_dir, *, knn_k):
|
|
68
|
+
"""Read real samples and construct their coordinate-based KNN graphs once."""
|
|
69
|
+
input_folder = Path(input_dir)
|
|
71
70
|
sample_names = _read_lines(input_folder / "ImageNameList.txt")
|
|
72
71
|
labels_by_sample = {
|
|
73
72
|
sample: _read_lines(input_folder / f"{sample}_CellTypeLabel.txt")
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
from itertools import combinations
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
import numpy as np
|
|
7
|
-
import pandas as pd
|
|
8
|
-
from scipy.stats import hypergeom,
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from scipy.stats import hypergeom, ttest_ind
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
MIN_FLOAT_P = np.finfo(np.float64).tiny
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
def read_nonempty_lines(path):
|
|
@@ -218,7 +221,7 @@ def overlap_coefficient(first, second):
|
|
|
218
221
|
return len(first & second) / smaller_size if smaller_size else 0.0
|
|
219
222
|
|
|
220
223
|
|
|
221
|
-
def _load_dominant_sets(samples, score_dir, score_threshold):
|
|
224
|
+
def _load_dominant_sets(samples, score_dir, score_threshold):
|
|
222
225
|
celltypes = (
|
|
223
226
|
pd.read_csv(Path(score_dir) / "CellTypeVec_List.csv")
|
|
224
227
|
.iloc[:, 0]
|
|
@@ -241,39 +244,42 @@ def _load_dominant_sets(samples, score_dir, score_threshold):
|
|
|
241
244
|
set(celltypes[matrix[:, cn] > score_threshold])
|
|
242
245
|
for cn in range(matrix.shape[1])
|
|
243
246
|
]
|
|
244
|
-
return dominant_sets, num_cn
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def
|
|
248
|
-
real_values = np.asarray(real_values, dtype=float)
|
|
249
|
-
background_values = np.asarray(background_values, dtype=float)
|
|
250
|
-
if len(real_values) >=
|
|
251
|
-
result =
|
|
252
|
-
real_values,
|
|
253
|
-
background_values,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
else
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
background_mean = (
|
|
264
|
-
np.mean(background_values) if len(background_values) else np.nan
|
|
247
|
+
return dominant_sets, num_cn
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _summarize_ttest(cn, real_values, background_values):
|
|
251
|
+
real_values = np.asarray(real_values, dtype=float)
|
|
252
|
+
background_values = np.asarray(background_values, dtype=float)
|
|
253
|
+
if len(real_values) >= 2 and len(background_values) >= 2:
|
|
254
|
+
result = ttest_ind(
|
|
255
|
+
real_values,
|
|
256
|
+
background_values,
|
|
257
|
+
equal_var=False,
|
|
258
|
+
alternative="greater",
|
|
259
|
+
)
|
|
260
|
+
t_statistic, p_value = float(result.statistic), float(result.pvalue)
|
|
261
|
+
else:
|
|
262
|
+
t_statistic, p_value = np.nan, np.nan
|
|
263
|
+
real_mean = np.mean(real_values) if len(real_values) else np.nan
|
|
264
|
+
background_mean = (
|
|
265
|
+
np.mean(background_values) if len(background_values) else np.nan
|
|
265
266
|
)
|
|
266
267
|
return {
|
|
267
|
-
"CN": cn,
|
|
268
|
-
"n_real": len(real_values),
|
|
269
|
-
"n_background": len(background_values),
|
|
270
|
-
"real_mean": real_mean,
|
|
271
|
-
"
|
|
272
|
-
"
|
|
273
|
-
"
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
"CN": cn,
|
|
269
|
+
"n_real": len(real_values),
|
|
270
|
+
"n_background": len(background_values),
|
|
271
|
+
"real_mean": real_mean,
|
|
272
|
+
"real_std": np.std(real_values, ddof=1) if len(real_values) > 1 else np.nan,
|
|
273
|
+
"background_mean": background_mean,
|
|
274
|
+
"background_std": (
|
|
275
|
+
np.std(background_values, ddof=1)
|
|
276
|
+
if len(background_values) > 1
|
|
277
|
+
else np.nan
|
|
278
|
+
),
|
|
279
|
+
"delta_mean": real_mean - background_mean,
|
|
280
|
+
"t_statistic": t_statistic,
|
|
281
|
+
"p_value_one_sided": p_value,
|
|
282
|
+
}
|
|
277
283
|
|
|
278
284
|
|
|
279
285
|
def calculate_recurrence(
|
|
@@ -316,21 +322,36 @@ def calculate_recurrence(
|
|
|
316
322
|
)
|
|
317
323
|
background_df = pd.DataFrame({"OverlapCoefficient": background_values})
|
|
318
324
|
|
|
319
|
-
|
|
320
|
-
for cn in [f"CN{index + 1}" for index in range(num_cn)]:
|
|
321
|
-
values = real_df.loc[
|
|
322
|
-
real_df["CN"] == cn, "OverlapCoefficient"
|
|
323
|
-
].to_numpy()
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
325
|
+
ttest_rows = []
|
|
326
|
+
for cn in [f"CN{index + 1}" for index in range(num_cn)]:
|
|
327
|
+
values = real_df.loc[
|
|
328
|
+
real_df["CN"] == cn, "OverlapCoefficient"
|
|
329
|
+
].to_numpy()
|
|
330
|
+
ttest_rows.append(_summarize_ttest(cn, values, background_values))
|
|
331
|
+
ttest_rows.append(
|
|
332
|
+
_summarize_ttest(
|
|
333
|
+
"Overall", real_df["OverlapCoefficient"].to_numpy(), background_values
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
ttest_df = pd.DataFrame(ttest_rows)
|
|
337
|
+
cn_mask = ttest_df["CN"] != "Overall"
|
|
338
|
+
adjusted_pvalues = bh_adjust(
|
|
339
|
+
ttest_df.loc[cn_mask, "p_value_one_sided"].to_numpy()
|
|
340
|
+
)
|
|
341
|
+
ttest_df["p_adjusted_bh"] = np.nan
|
|
342
|
+
ttest_df["RecurrenceScore"] = np.nan
|
|
343
|
+
ttest_df["SignificantRecurrence"] = False
|
|
344
|
+
ttest_df.loc[cn_mask, "p_adjusted_bh"] = adjusted_pvalues
|
|
345
|
+
ttest_df.loc[cn_mask, "RecurrenceScore"] = -np.log10(
|
|
346
|
+
np.maximum(adjusted_pvalues, MIN_FLOAT_P)
|
|
347
|
+
)
|
|
348
|
+
ttest_df.loc[cn_mask, "SignificantRecurrence"] = (
|
|
349
|
+
(
|
|
350
|
+
ttest_df.loc[cn_mask, "p_adjusted_bh"]
|
|
351
|
+
< recurrence_p_threshold
|
|
352
|
+
)
|
|
353
|
+
& (ttest_df.loc[cn_mask, "delta_mean"] > 0)
|
|
354
|
+
)
|
|
334
355
|
|
|
335
356
|
output_dir = Path(output_dir)
|
|
336
357
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -338,11 +359,11 @@ def calculate_recurrence(
|
|
|
338
359
|
background_df.to_csv(
|
|
339
360
|
output_dir / "BackgroundDistribution_randomCN.csv", index=False
|
|
340
361
|
)
|
|
341
|
-
|
|
342
|
-
return real_df, background_df,
|
|
362
|
+
ttest_df.to_csv(output_dir / "TTest_Results.csv", index=False)
|
|
363
|
+
return real_df, background_df, ttest_df
|
|
343
364
|
|
|
344
365
|
|
|
345
|
-
def evaluate_cn_num(
|
|
366
|
+
def evaluate_cn_num(
|
|
346
367
|
num_cn,
|
|
347
368
|
dataset,
|
|
348
369
|
celltype_file,
|
|
@@ -368,60 +389,38 @@ def evaluate_cn_num(
|
|
|
368
389
|
cn_label_dir=cn_label_dir,
|
|
369
390
|
hard_assignments=hard_assignments,
|
|
370
391
|
)
|
|
371
|
-
real_df, _,
|
|
372
|
-
enrichment_dir,
|
|
373
|
-
Path(dataset) / "ImageNameList.txt",
|
|
374
|
-
overlap_dir,
|
|
392
|
+
real_df, _, ttest_df = calculate_recurrence(
|
|
393
|
+
enrichment_dir,
|
|
394
|
+
Path(dataset) / "ImageNameList.txt",
|
|
395
|
+
overlap_dir,
|
|
375
396
|
score_threshold=-np.log10(enrichment_p_threshold),
|
|
376
397
|
recurrence_p_threshold=recurrence_p_threshold,
|
|
377
|
-
n_random=n_random,
|
|
378
|
-
seed=seed,
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
mean_rs = float(per_cn["RecurrenceScore"].mean())
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"
|
|
393
|
-
|
|
398
|
+
n_random=n_random,
|
|
399
|
+
seed=seed,
|
|
400
|
+
)
|
|
401
|
+
per_cn = ttest_df[ttest_df["CN"] != "Overall"]
|
|
402
|
+
significant_mask = per_cn["SignificantRecurrence"].astype(bool)
|
|
403
|
+
significant_cn = int(significant_mask.sum())
|
|
404
|
+
significant_fraction = significant_cn / num_cn
|
|
405
|
+
mean_rs = float(per_cn["RecurrenceScore"].mean())
|
|
406
|
+
mean_oc = float(real_df["OverlapCoefficient"].mean())
|
|
407
|
+
score = float(
|
|
408
|
+
significant_fraction**2 * significant_cn * mean_rs
|
|
409
|
+
)
|
|
410
|
+
result = {
|
|
411
|
+
"NumCN": int(num_cn),
|
|
412
|
+
"SignificantCN": significant_cn,
|
|
413
|
+
"SignificantFraction": significant_fraction,
|
|
414
|
+
"MeanRS": mean_rs,
|
|
415
|
+
"MeanOC": mean_oc,
|
|
416
|
+
"Score": score,
|
|
417
|
+
}
|
|
394
418
|
pd.DataFrame([result]).to_csv(output_dir / "CNNumScore.csv", index=False)
|
|
395
|
-
return result, per_cn.copy()
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
def
|
|
399
|
-
"""
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
metrics = evaluate_cn_num(
|
|
404
|
-
num_cn=result.selected_cn,
|
|
405
|
-
dataset=paths.input_dir,
|
|
406
|
-
cn_label_dir=result.hard_assignment_dir,
|
|
407
|
-
celltype_file=result.unique_celltypes_file,
|
|
408
|
-
output_dir=paths.downstream_data,
|
|
409
|
-
enrichment_p_threshold=config.enrichment.p_value_threshold,
|
|
410
|
-
minimum_adjusted_p=config.enrichment.minimum_adjusted_p,
|
|
411
|
-
recurrence_p_threshold=config.recurrence.recurrence_p_threshold,
|
|
412
|
-
n_random=config.recurrence.null_draws,
|
|
413
|
-
seed=config.random_seed,
|
|
414
|
-
)
|
|
415
|
-
export_enrichment_long(
|
|
416
|
-
dataset=paths.input_dir,
|
|
417
|
-
enrichment_dir=paths.enrichment,
|
|
418
|
-
output_file=paths.communication_config / "EnrichScoreMatrix_long.csv",
|
|
419
|
-
)
|
|
420
|
-
return metrics
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
def select_cn_num(results):
|
|
424
|
-
"""Select the K with the maximum CRI, preferring smaller K on ties."""
|
|
425
|
-
if not results:
|
|
426
|
-
raise ValueError("No CN-number results were supplied")
|
|
427
|
-
return min(results, key=lambda row: (-row["CRI"], row["NumCN"])).copy()
|
|
419
|
+
return result, per_cn.copy()
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def select_cn_num(results):
|
|
423
|
+
"""Select maximum score, using smaller K as the deterministic tie-break."""
|
|
424
|
+
if not results:
|
|
425
|
+
raise ValueError("No CN-number results were supplied")
|
|
426
|
+
return min(results, key=lambda row: (-row["Score"], row["NumCN"]))
|
|
@@ -14,16 +14,19 @@ import torch
|
|
|
14
14
|
import torch.nn.functional as F
|
|
15
15
|
from torch_geometric.loader import DataLoader
|
|
16
16
|
|
|
17
|
-
from .model import CNModel
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def set_reproducible_seed(seed):
|
|
17
|
+
from .model import CNModel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def set_reproducible_seed(seed):
|
|
21
21
|
random.seed(seed)
|
|
22
22
|
np.random.seed(seed)
|
|
23
23
|
torch.manual_seed(seed)
|
|
24
24
|
torch.cuda.manual_seed_all(seed)
|
|
25
25
|
torch.backends.cudnn.benchmark = False
|
|
26
26
|
torch.backends.cudnn.deterministic = True
|
|
27
|
+
if hasattr(torch.backends, "cuda") and hasattr(torch.backends.cuda, "matmul"):
|
|
28
|
+
torch.backends.cuda.matmul.allow_tf32 = False
|
|
29
|
+
torch.backends.cudnn.allow_tf32 = False
|
|
27
30
|
torch.use_deterministic_algorithms(True, warn_only=True)
|
|
28
31
|
|
|
29
32
|
|
|
@@ -133,10 +136,16 @@ def train_one_run(
|
|
|
133
136
|
shuffle=True,
|
|
134
137
|
pin_memory=True,
|
|
135
138
|
generator=loader_generator,
|
|
136
|
-
)
|
|
137
|
-
if device is None:
|
|
138
|
-
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
139
|
-
|
|
139
|
+
)
|
|
140
|
+
if device is None:
|
|
141
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
142
|
+
else:
|
|
143
|
+
device = torch.device(device)
|
|
144
|
+
if device.type == "cuda":
|
|
145
|
+
print(f" Training device: GPU ({torch.cuda.get_device_name(device)})")
|
|
146
|
+
else:
|
|
147
|
+
print(" Training device: CPU")
|
|
148
|
+
model = CNModel(dataset.num_features, config).to(device)
|
|
140
149
|
optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate)
|
|
141
150
|
|
|
142
151
|
loss_csv = output_folder / "Epoch_TrainLoss.csv"
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cytocommunity2
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Weakly supervised cellular-neighborhood learning for spatial omics
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: matplotlib>=3.6
|
|
9
|
+
Requires-Dist: numpy<2,>=1.23
|
|
10
|
+
Requires-Dist: pandas<3,>=1.5
|
|
11
|
+
Requires-Dist: sci-palettes
|
|
12
|
+
Requires-Dist: scikit-learn>=1.1
|
|
13
|
+
Requires-Dist: scipy>=1.9
|
|
14
|
+
Requires-Dist: seaborn>=0.12
|
|
15
|
+
Requires-Dist: torch-geometric>=2.0
|
|
16
|
+
Provides-Extra: downstream
|
|
17
|
+
Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
|
|
18
|
+
Requires-Dist: jupyter; extra == "downstream"
|
|
19
|
+
Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# CytoCommunity2
|
|
23
|
+
|
|
24
|
+
## Contents
|
|
25
|
+
|
|
26
|
+
- [Overview](#overview)
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Maintainers](#maintainers)
|
|
30
|
+
- [Citation](#citation)
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
|
|
34
|
+
To enhance CytoCommunity (https://github.com/huBioinfo/CytoCommunity), we
|
|
35
|
+
present CytoCommunity2, **a unified weakly-supervised framework** for
|
|
36
|
+
identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across
|
|
37
|
+
**large-scale spatial omics samples with single or multiple biological
|
|
38
|
+
conditions**.
|
|
39
|
+
|
|
40
|
+
Inspired by histopathology workflows, CytoCommunity2 first hierarchically
|
|
41
|
+
partitions the large single-cell spatial map into small patches, performs graph
|
|
42
|
+
construction and weakly supervised TCN learning for each patch, and finally
|
|
43
|
+
merges results through KNN-based TCN reassignment at segmentation boundaries to
|
|
44
|
+
ensure TCN spatial continuity. This strategy divides the original sample into
|
|
45
|
+
patches for TCN learning, achieving **memory efficiency (typical 24G graphics
|
|
46
|
+
memory is enough)** and also increased sample throughput. These optimizations
|
|
47
|
+
significantly **enhance the robustness of TCNs and cross-sample alignment
|
|
48
|
+
performance**.
|
|
49
|
+
|
|
50
|
+
Furthermore, to make CytoCommunity2 a unified framework that is also
|
|
51
|
+
**applicable for single-condition spatial omics datasets**, pseudo-samples with
|
|
52
|
+
artificial labels are generated, enabling automatic TCN alignment across real
|
|
53
|
+
samples via **contrastive learning**.
|
|
54
|
+
|
|
55
|
+
**_In summary, the advantages of CytoCommunity2 include:_**
|
|
56
|
+
|
|
57
|
+
**_(1) Using significantly less memory for large-scale spatial omics samples
|
|
58
|
+
with millions of cells._**
|
|
59
|
+
|
|
60
|
+
**_(2) A unified weakly-supervised model applicable for both multi-condition
|
|
61
|
+
and single-condition datasets._**
|
|
62
|
+
|
|
63
|
+
**_(3) High TCN alignment performance makes it well-suited for comparative
|
|
64
|
+
analysis in large cohort studies._**
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
### Hardware requirement
|
|
69
|
+
|
|
70
|
+
Graphics memory: 24G
|
|
71
|
+
|
|
72
|
+
Storage: 10GB or more
|
|
73
|
+
|
|
74
|
+
### Install CytoCommunity2
|
|
75
|
+
|
|
76
|
+
CytoCommunity2 requires Python 3.10 or newer. We recommend creating and
|
|
77
|
+
activating a Conda environment first:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
conda create -n cytocommunity2 python=3.10
|
|
81
|
+
conda activate cytocommunity2
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Install PyTorch for your compute platform before installing CytoCommunity2.
|
|
85
|
+
For an NVIDIA GPU on Windows or Linux, first install the appropriate
|
|
86
|
+
CUDA-enabled PyTorch build from the
|
|
87
|
+
[official PyTorch installation page](https://pytorch.org/get-started/locally/).
|
|
88
|
+
|
|
89
|
+
Install the package:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install cytocommunity2
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
To run the downstream analyses, install the
|
|
96
|
+
optional downstream dependencies with:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install "cytocommunity2[downstream]"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This extra includes `esda`, `libpysal`, and Jupyter; they are not installed
|
|
103
|
+
by the basic command above.
|
|
104
|
+
|
|
105
|
+
## Usage
|
|
106
|
+
|
|
107
|
+
### 1. Prepare input data
|
|
108
|
+
|
|
109
|
+
The input data to CytoCommunity2 includes four types of files :
|
|
110
|
+
|
|
111
|
+
(1) An image (sample) name list file, named as "ImageNameList.txt".
|
|
112
|
+
|
|
113
|
+
(2) A cell type label file for each image (sample), named as "[image
|
|
114
|
+
name]_CellTypeLabel.txt". Note that [image_name] should be consistent with your
|
|
115
|
+
customized image names listed in the "ImageNameList.txt". This file lists cell
|
|
116
|
+
type names of all cells in an image (sample).
|
|
117
|
+
|
|
118
|
+
(3) A cell spatial coordinate file for each image (sample), named as "[image
|
|
119
|
+
name]_Coordinates.txt". Note that [image_name] should be consistent with your
|
|
120
|
+
customized image names listed in the "ImageNameList.txt". This file lists cell
|
|
121
|
+
coordinates (tab-delimited x/y) of all cells in an image (sample). The cell
|
|
122
|
+
orders should be exactly the same with "[image name]_CellTypeLabel.txt".
|
|
123
|
+
|
|
124
|
+
(4) A graph label file for each image (sample), named as "[image
|
|
125
|
+
name]_GraphLabel.txt". For multi-condition datasets, this file must be provided
|
|
126
|
+
for every image (sample) and contain one integer indicating its biological
|
|
127
|
+
condition. For single-condition datasets, graph label
|
|
128
|
+
files can be omitted for all images (samples), and CytoCommunity2 will generate
|
|
129
|
+
pseudo-samples for weakly-supervised learning.
|
|
130
|
+
|
|
131
|
+
### 2. Configure CytoCommunity2
|
|
132
|
+
|
|
133
|
+
Before running CytoCommunity2, set `input_dir` and review all
|
|
134
|
+
model and downstream parameters in
|
|
135
|
+
[`hyperparameters.json`](https://github.com/LiukangWu/CytoCommunity2/blob/main/hyperparameters.json). The Python workflow will use this configuration file.
|
|
136
|
+
|
|
137
|
+
### 3. Run CytoCommunity2
|
|
138
|
+
|
|
139
|
+
The tutorial uses a TNBC dataset as a worked example and demonstrates the
|
|
140
|
+
entire CytoCommunity2 workflow, from model training and CN identification to
|
|
141
|
+
downstream analysis:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
jupyter notebook tutorial/TNBC_workflow.ipynb
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 4. Output files
|
|
148
|
+
|
|
149
|
+
The main output directories are:
|
|
150
|
+
|
|
151
|
+
- `CNLearning_Output/`: CN-number screening, trained models and CN labels.
|
|
152
|
+
- `CNVisualization_Output/`: spatial plots and result tables.
|
|
153
|
+
- `data/`: downstream analysis results and intermediate data tables.
|
|
154
|
+
- `plot/`: figures generated by downstream analyses.
|
|
155
|
+
|
|
156
|
+
## Maintainers
|
|
157
|
+
|
|
158
|
+
- Liukang Wu (yetong@stu.xidian.edu.cn)
|
|
159
|
+
- Yafei Xu (22031212416@stu.xidian.edu.cn)
|
|
160
|
+
- Yuxuan Hu (huyuxuan@xidian.edu.cn)
|
|
161
|
+
|
|
162
|
+
## Citation
|
|
163
|
+
|
|
164
|
+
Yuxuan Hu, Jiazhen Rong, Yafei Xu, Runzhi Xie, Jacqueline Peng, Lin Gao, and
|
|
165
|
+
Kai Tan. “Unsupervised and supervised discovery of tissue cellular
|
|
166
|
+
neighborhoods from cell phenotypes.” *Nature Methods* 21 (2024): 267–278.
|
|
167
|
+
https://doi.org/10.1038/s41592-023-02124-2
|
cytocommunity2-0.1.1/PKG-INFO
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: cytocommunity2
|
|
3
|
-
Version: 0.1.1
|
|
4
|
-
Summary: Weakly supervised cellular-neighborhood learning for spatial omics
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: matplotlib>=3.6
|
|
8
|
-
Requires-Dist: numpy<2,>=1.23
|
|
9
|
-
Requires-Dist: pandas<3,>=1.5
|
|
10
|
-
Requires-Dist: sci-palettes
|
|
11
|
-
Requires-Dist: scikit-learn>=1.1
|
|
12
|
-
Requires-Dist: scipy>=1.9
|
|
13
|
-
Requires-Dist: seaborn>=0.12
|
|
14
|
-
Requires-Dist: torch>=1.11
|
|
15
|
-
Requires-Dist: torch-geometric>=2.0
|
|
16
|
-
Provides-Extra: downstream
|
|
17
|
-
Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
|
|
18
|
-
Requires-Dist: jupyter; extra == "downstream"
|
|
19
|
-
Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
|
|
20
|
-
|
|
21
|
-
# CytoCommunity2
|
|
22
|
-
|
|
23
|
-
CytoCommunity2 is a Python package for identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across large-scale spatial omics samples with single or multiple biological conditions .
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
Install the core package with `python -m pip install cytocommunity2`. For the full workflow, use `python -m pip install "cytocommunity2[downstream]"`.
|
cytocommunity2-0.1.1/README.md
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# CytoCommunity2
|
|
2
|
-
|
|
3
|
-
CytoCommunity2 is a Python package for identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across large-scale spatial omics samples with single or multiple biological conditions .
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
Install the core package with `python -m pip install cytocommunity2`. For the full workflow, use `python -m pip install "cytocommunity2[downstream]"`.
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: cytocommunity2
|
|
3
|
-
Version: 0.1.1
|
|
4
|
-
Summary: Weakly supervised cellular-neighborhood learning for spatial omics
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: matplotlib>=3.6
|
|
8
|
-
Requires-Dist: numpy<2,>=1.23
|
|
9
|
-
Requires-Dist: pandas<3,>=1.5
|
|
10
|
-
Requires-Dist: sci-palettes
|
|
11
|
-
Requires-Dist: scikit-learn>=1.1
|
|
12
|
-
Requires-Dist: scipy>=1.9
|
|
13
|
-
Requires-Dist: seaborn>=0.12
|
|
14
|
-
Requires-Dist: torch>=1.11
|
|
15
|
-
Requires-Dist: torch-geometric>=2.0
|
|
16
|
-
Provides-Extra: downstream
|
|
17
|
-
Requires-Dist: esda<2.6,>=2.5; extra == "downstream"
|
|
18
|
-
Requires-Dist: jupyter; extra == "downstream"
|
|
19
|
-
Requires-Dist: libpysal<4.10,>=4.9; extra == "downstream"
|
|
20
|
-
|
|
21
|
-
# CytoCommunity2
|
|
22
|
-
|
|
23
|
-
CytoCommunity2 is a Python package for identifying and comparing tissue cellular neighborhoods (TCNs or CNs) across large-scale spatial omics samples with single or multiple biological conditions .
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
Install the core package with `python -m pip install cytocommunity2`. For the full workflow, use `python -m pip install "cytocommunity2[downstream]"`.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/between.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/utils.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/communication/within.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/__init__.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/cn_selection.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/coherence.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/common.py
RENAMED
|
File without changes
|
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/composition.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/dominant.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/dotplots.py
RENAMED
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2/downstream/plotting/recurrence.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cytocommunity2-0.1.1 → cytocommunity2-0.1.2}/src/cytocommunity2.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|