citegeist 0.1.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.
- citegeist-0.1.0/LICENSE +28 -0
- citegeist-0.1.0/PKG-INFO +199 -0
- citegeist-0.1.0/README.md +158 -0
- citegeist-0.1.0/citegeist/__init__.py +0 -0
- citegeist-0.1.0/citegeist/core/__init__.py +21 -0
- citegeist-0.1.0/citegeist/core/analysis_functions.py +42 -0
- citegeist-0.1.0/citegeist/core/checkpoints.py +174 -0
- citegeist-0.1.0/citegeist/core/citegeist_model.py +807 -0
- citegeist-0.1.0/citegeist/core/gurobi_impl.py +1219 -0
- citegeist-0.1.0/citegeist/core/utils.py +378 -0
- citegeist-0.1.0/citegeist/py.typed +0 -0
- citegeist-0.1.0/citegeist.egg-info/PKG-INFO +199 -0
- citegeist-0.1.0/citegeist.egg-info/SOURCES.txt +21 -0
- citegeist-0.1.0/citegeist.egg-info/dependency_links.txt +1 -0
- citegeist-0.1.0/citegeist.egg-info/requires.txt +13 -0
- citegeist-0.1.0/citegeist.egg-info/top_level.txt +1 -0
- citegeist-0.1.0/pyproject.toml +6 -0
- citegeist-0.1.0/setup.cfg +4 -0
- citegeist-0.1.0/setup.py +41 -0
- citegeist-0.1.0/tests/sbatch_CITEgeistArray.sh +71 -0
- citegeist-0.1.0/tests/test_citegeist_simulated.py +381 -0
- citegeist-0.1.0/tests/visualize_prop_gridsearch.py +147 -0
- citegeist-0.1.0/tests/visualize_simulated_benchmarks.py +184 -0
citegeist-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Alexander Chang and Brent T. Schlegel
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
citegeist-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: citegeist
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A spatial transcriptomics deconvolution tool for cell type identification and gene expression analysis
|
|
5
|
+
Home-page: https://github.com/acc383/CITEgeist
|
|
6
|
+
Author: Alexander Chang, Brent T. Schlegel
|
|
7
|
+
Author-email: acc383@cornell.edu
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=1.20.0
|
|
20
|
+
Requires-Dist: pandas>=1.3.0
|
|
21
|
+
Requires-Dist: scanpy>=1.9.0
|
|
22
|
+
Requires-Dist: scipy>=1.7.0
|
|
23
|
+
Requires-Dist: pyarrow>=5.0.0
|
|
24
|
+
Requires-Dist: gurobipy>=9.5.0
|
|
25
|
+
Requires-Dist: squidpy>=1.6.2
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: provides-extra
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
41
|
+
|
|
42
|
+
# CITEgeist: Cellular Indexing of Transcriptomes and Epitopes for Guided Exploration of Intrinsic Spatial Trends
|
|
43
|
+
|
|
44
|
+
CITEgeist is a computational method for deconvolving spatial transcriptomics data using spatially-resolved CITE-seq measurements. The pipeline performs both cell-type proportion estimation and gene expression deconvolution in a two-pass approach, leveraging both protein and RNA measurements from the same spatial locations.
|
|
45
|
+
|
|
46
|
+
## Quick Installation
|
|
47
|
+
|
|
48
|
+
You can now also install CITEgeist using pip:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install citegeist
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Table of Contents
|
|
55
|
+
1. [System Requirements](#system-requirements)
|
|
56
|
+
- [Software Dependencies](#software-dependencies)
|
|
57
|
+
- [Hardware Requirements](#hardware-requirements)
|
|
58
|
+
2. [Getting Started](#getting-started)
|
|
59
|
+
- [1. Download the Code and Data](#1-download-the-code-and-data)
|
|
60
|
+
- [2. Set Up the Environment](#2-set-up-the-environment)
|
|
61
|
+
- [3. Obtain Gurobi License](#3-obtain-gurobi-license)
|
|
62
|
+
- [4. Running CITEgeist](#4-running-citegeist)
|
|
63
|
+
3. [Benchmarking and Reproducibility](#benchmarking-and-reproducibility)
|
|
64
|
+
4. [Run the Analysis](#run-the-analysis)
|
|
65
|
+
|
|
66
|
+
## System Requirements
|
|
67
|
+
|
|
68
|
+
### Software Dependencies
|
|
69
|
+
|
|
70
|
+
- **Operating System**:
|
|
71
|
+
- Linux
|
|
72
|
+
- macOS
|
|
73
|
+
- Windows 10 with WSL2
|
|
74
|
+
|
|
75
|
+
- **Python**: 3.10
|
|
76
|
+
- **Gurobi** [version > 3.9](https://www.gurobi.com/downloads/gurobi-software/)
|
|
77
|
+
#### Key Python Dependencies
|
|
78
|
+
- scanpy==1.10.4
|
|
79
|
+
- anndata==0.11.3
|
|
80
|
+
- numpy==1.26.4
|
|
81
|
+
- pandas==2.2.3
|
|
82
|
+
- scipy==1.13.1
|
|
83
|
+
- scikit-learn==1.6.1
|
|
84
|
+
- gurobipy==11.0.2 (requires license)
|
|
85
|
+
- matplotlib==3.10.0
|
|
86
|
+
- seaborn==0.13.2
|
|
87
|
+
- h5py==3.12.1
|
|
88
|
+
- squidpy==1.6.2
|
|
89
|
+
- spatialdata==0.2.5.post0
|
|
90
|
+
|
|
91
|
+
It is recommended to install the dependencies in the `CITEgeist_env.yml` file for running the notebooks.
|
|
92
|
+
|
|
93
|
+
### Hardware Requirements
|
|
94
|
+
- **RAM**: Minimum 16GB, Recommended 64GB+
|
|
95
|
+
- **Storage**: 16GB minimum for installation and basic analysis
|
|
96
|
+
- **CPU**: Multi-core processor recommended (8+ cores for optimal performance)
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Getting Started
|
|
101
|
+
|
|
102
|
+
### 1. Download the Code and Data (Instructions for Peer Reviewers)
|
|
103
|
+
|
|
104
|
+
1. Download the code from Figshare: [https://figshare.com/s/34e456fd7786e5211acc](https://figshare.com/s/34e456fd7786e5211acc)
|
|
105
|
+
2. Unzip the downloaded file to your preferred location.
|
|
106
|
+
3. Download the data from GEO (Reviewers: see Data Availability section in the manuscript for the GEO link and Private Access Token).
|
|
107
|
+
4. Run the following code to strip unique identifiers required by GEO:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# go to the 'data' directory
|
|
111
|
+
cd data
|
|
112
|
+
|
|
113
|
+
# untar the raw files
|
|
114
|
+
mkdir -pv ./GEO_data
|
|
115
|
+
tar -xvf GEO_data_RAW.tar -C ./GEO_data
|
|
116
|
+
|
|
117
|
+
# run the py preprocessing script
|
|
118
|
+
## round 1) aggregate the files by sample
|
|
119
|
+
python3 ./delete_all_but_essential.py --folder GEO_data # select option: 1
|
|
120
|
+
|
|
121
|
+
## round 2) remove the prefix from necessary files
|
|
122
|
+
python3 ./delete_all_but_essential.py --folder GEO_data # select option: 2
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Note: When prompted, select Option 1 or 2 and type 'Yes' to confirm.
|
|
126
|
+
|
|
127
|
+
### 2. Set Up the Environment
|
|
128
|
+
|
|
129
|
+
- Install dependencies using the provided environment file:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
conda env create -f CITEgeist_env.yml
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- Activate the environment and set up a Jupyter kernel:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
conda activate CITEgeist_env
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 3. Obtain Gurobi License
|
|
142
|
+
|
|
143
|
+
CITEgeist requires a Gurobi license (free for academic use):
|
|
144
|
+
|
|
145
|
+
1. Sign up for an academic license at: [https://www.gurobi.com/downloads/end-user-license-agreement-academic/](https://www.gurobi.com/downloads/end-user-license-agreement-academic/)
|
|
146
|
+
2. Follow the instructions to download and install your license.
|
|
147
|
+
3. Update the license file path in the notebooks to match your local license location.
|
|
148
|
+
|
|
149
|
+
### 4. Running CITEgeist
|
|
150
|
+
|
|
151
|
+
You can run CITEgeist in two ways:
|
|
152
|
+
|
|
153
|
+
#### A. Using Jupyter Notebooks
|
|
154
|
+
- Update data paths in the top of the notebooks to match your local directory structure.
|
|
155
|
+
- Expected runtime on a standard computer (16 threads, 32GB RAM):
|
|
156
|
+
- Vignette 1: ~2 hours
|
|
157
|
+
- Vignette 2: ~2 hours
|
|
158
|
+
- Vignette 3: ~10 hours
|
|
159
|
+
|
|
160
|
+
#### Key Parameters:
|
|
161
|
+
- `radius`: Radius for neighbor detection (default: 4)
|
|
162
|
+
- `lambda_reg`: Regularization strength for cell proportion estimation (default: 0.001)
|
|
163
|
+
- `alpha_elastic`: Elastic net mixing parameter for cell proportion estimation (default: 0.7)
|
|
164
|
+
- `max_y_change`: Maximum allowed change in Y values (default: 0.2)
|
|
165
|
+
|
|
166
|
+
#### Optional Parameters:
|
|
167
|
+
- `profiling_only`: Set for cell-type proportions only.
|
|
168
|
+
- `max_workers`: Number of parallel workers.
|
|
169
|
+
- `checkpoint_interval`: Checkpoint saving interval.
|
|
170
|
+
|
|
171
|
+
#### B. Using SLURM Distribution
|
|
172
|
+
For large-scale analyses, you can use the provided `CITEgeist/examples/sbatch_sample.sh` script for distributed computing.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Benchmarking and Reproducibility
|
|
177
|
+
|
|
178
|
+
For specific reproduction of benchmarking tests and detailed methodology, please refer to the 'examples' and 'benchmarking' section in the documentation.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Run the Analysis
|
|
183
|
+
|
|
184
|
+
You can either:
|
|
185
|
+
|
|
186
|
+
#### A. Run the notebooks directly:
|
|
187
|
+
- Update data paths in the notebooks to match your local directory structure.
|
|
188
|
+
- Expected runtime: ~2 hours on a standard computer (16 threads, 32GB RAM).
|
|
189
|
+
|
|
190
|
+
#### B. Use SLURM distribution:
|
|
191
|
+
- Use the provided `examples/sbatch_sample.sh` script for distributed computing.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Additional System Requirements
|
|
196
|
+
- **RAM**: 32GB (minimum)
|
|
197
|
+
- **CPU**: 16 threads (recommended)
|
|
198
|
+
- **Storage**: Sufficient space for the GEO dataset
|
|
199
|
+
- **Operating System**: Linux/Unix recommended (Windows users may need additional configuration)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# CITEgeist: Cellular Indexing of Transcriptomes and Epitopes for Guided Exploration of Intrinsic Spatial Trends
|
|
2
|
+
|
|
3
|
+
CITEgeist is a computational method for deconvolving spatial transcriptomics data using spatially-resolved CITE-seq measurements. The pipeline performs both cell-type proportion estimation and gene expression deconvolution in a two-pass approach, leveraging both protein and RNA measurements from the same spatial locations.
|
|
4
|
+
|
|
5
|
+
## Quick Installation
|
|
6
|
+
|
|
7
|
+
You can now also install CITEgeist using pip:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install citegeist
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
1. [System Requirements](#system-requirements)
|
|
15
|
+
- [Software Dependencies](#software-dependencies)
|
|
16
|
+
- [Hardware Requirements](#hardware-requirements)
|
|
17
|
+
2. [Getting Started](#getting-started)
|
|
18
|
+
- [1. Download the Code and Data](#1-download-the-code-and-data)
|
|
19
|
+
- [2. Set Up the Environment](#2-set-up-the-environment)
|
|
20
|
+
- [3. Obtain Gurobi License](#3-obtain-gurobi-license)
|
|
21
|
+
- [4. Running CITEgeist](#4-running-citegeist)
|
|
22
|
+
3. [Benchmarking and Reproducibility](#benchmarking-and-reproducibility)
|
|
23
|
+
4. [Run the Analysis](#run-the-analysis)
|
|
24
|
+
|
|
25
|
+
## System Requirements
|
|
26
|
+
|
|
27
|
+
### Software Dependencies
|
|
28
|
+
|
|
29
|
+
- **Operating System**:
|
|
30
|
+
- Linux
|
|
31
|
+
- macOS
|
|
32
|
+
- Windows 10 with WSL2
|
|
33
|
+
|
|
34
|
+
- **Python**: 3.10
|
|
35
|
+
- **Gurobi** [version > 3.9](https://www.gurobi.com/downloads/gurobi-software/)
|
|
36
|
+
#### Key Python Dependencies
|
|
37
|
+
- scanpy==1.10.4
|
|
38
|
+
- anndata==0.11.3
|
|
39
|
+
- numpy==1.26.4
|
|
40
|
+
- pandas==2.2.3
|
|
41
|
+
- scipy==1.13.1
|
|
42
|
+
- scikit-learn==1.6.1
|
|
43
|
+
- gurobipy==11.0.2 (requires license)
|
|
44
|
+
- matplotlib==3.10.0
|
|
45
|
+
- seaborn==0.13.2
|
|
46
|
+
- h5py==3.12.1
|
|
47
|
+
- squidpy==1.6.2
|
|
48
|
+
- spatialdata==0.2.5.post0
|
|
49
|
+
|
|
50
|
+
It is recommended to install the dependencies in the `CITEgeist_env.yml` file for running the notebooks.
|
|
51
|
+
|
|
52
|
+
### Hardware Requirements
|
|
53
|
+
- **RAM**: Minimum 16GB, Recommended 64GB+
|
|
54
|
+
- **Storage**: 16GB minimum for installation and basic analysis
|
|
55
|
+
- **CPU**: Multi-core processor recommended (8+ cores for optimal performance)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Getting Started
|
|
60
|
+
|
|
61
|
+
### 1. Download the Code and Data (Instructions for Peer Reviewers)
|
|
62
|
+
|
|
63
|
+
1. Download the code from Figshare: [https://figshare.com/s/34e456fd7786e5211acc](https://figshare.com/s/34e456fd7786e5211acc)
|
|
64
|
+
2. Unzip the downloaded file to your preferred location.
|
|
65
|
+
3. Download the data from GEO (Reviewers: see Data Availability section in the manuscript for the GEO link and Private Access Token).
|
|
66
|
+
4. Run the following code to strip unique identifiers required by GEO:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# go to the 'data' directory
|
|
70
|
+
cd data
|
|
71
|
+
|
|
72
|
+
# untar the raw files
|
|
73
|
+
mkdir -pv ./GEO_data
|
|
74
|
+
tar -xvf GEO_data_RAW.tar -C ./GEO_data
|
|
75
|
+
|
|
76
|
+
# run the py preprocessing script
|
|
77
|
+
## round 1) aggregate the files by sample
|
|
78
|
+
python3 ./delete_all_but_essential.py --folder GEO_data # select option: 1
|
|
79
|
+
|
|
80
|
+
## round 2) remove the prefix from necessary files
|
|
81
|
+
python3 ./delete_all_but_essential.py --folder GEO_data # select option: 2
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Note: When prompted, select Option 1 or 2 and type 'Yes' to confirm.
|
|
85
|
+
|
|
86
|
+
### 2. Set Up the Environment
|
|
87
|
+
|
|
88
|
+
- Install dependencies using the provided environment file:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
conda env create -f CITEgeist_env.yml
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- Activate the environment and set up a Jupyter kernel:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
conda activate CITEgeist_env
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. Obtain Gurobi License
|
|
101
|
+
|
|
102
|
+
CITEgeist requires a Gurobi license (free for academic use):
|
|
103
|
+
|
|
104
|
+
1. Sign up for an academic license at: [https://www.gurobi.com/downloads/end-user-license-agreement-academic/](https://www.gurobi.com/downloads/end-user-license-agreement-academic/)
|
|
105
|
+
2. Follow the instructions to download and install your license.
|
|
106
|
+
3. Update the license file path in the notebooks to match your local license location.
|
|
107
|
+
|
|
108
|
+
### 4. Running CITEgeist
|
|
109
|
+
|
|
110
|
+
You can run CITEgeist in two ways:
|
|
111
|
+
|
|
112
|
+
#### A. Using Jupyter Notebooks
|
|
113
|
+
- Update data paths in the top of the notebooks to match your local directory structure.
|
|
114
|
+
- Expected runtime on a standard computer (16 threads, 32GB RAM):
|
|
115
|
+
- Vignette 1: ~2 hours
|
|
116
|
+
- Vignette 2: ~2 hours
|
|
117
|
+
- Vignette 3: ~10 hours
|
|
118
|
+
|
|
119
|
+
#### Key Parameters:
|
|
120
|
+
- `radius`: Radius for neighbor detection (default: 4)
|
|
121
|
+
- `lambda_reg`: Regularization strength for cell proportion estimation (default: 0.001)
|
|
122
|
+
- `alpha_elastic`: Elastic net mixing parameter for cell proportion estimation (default: 0.7)
|
|
123
|
+
- `max_y_change`: Maximum allowed change in Y values (default: 0.2)
|
|
124
|
+
|
|
125
|
+
#### Optional Parameters:
|
|
126
|
+
- `profiling_only`: Set for cell-type proportions only.
|
|
127
|
+
- `max_workers`: Number of parallel workers.
|
|
128
|
+
- `checkpoint_interval`: Checkpoint saving interval.
|
|
129
|
+
|
|
130
|
+
#### B. Using SLURM Distribution
|
|
131
|
+
For large-scale analyses, you can use the provided `CITEgeist/examples/sbatch_sample.sh` script for distributed computing.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Benchmarking and Reproducibility
|
|
136
|
+
|
|
137
|
+
For specific reproduction of benchmarking tests and detailed methodology, please refer to the 'examples' and 'benchmarking' section in the documentation.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Run the Analysis
|
|
142
|
+
|
|
143
|
+
You can either:
|
|
144
|
+
|
|
145
|
+
#### A. Run the notebooks directly:
|
|
146
|
+
- Update data paths in the notebooks to match your local directory structure.
|
|
147
|
+
- Expected runtime: ~2 hours on a standard computer (16 threads, 32GB RAM).
|
|
148
|
+
|
|
149
|
+
#### B. Use SLURM distribution:
|
|
150
|
+
- Use the provided `examples/sbatch_sample.sh` script for distributed computing.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Additional System Requirements
|
|
155
|
+
- **RAM**: 32GB (minimum)
|
|
156
|
+
- **CPU**: 16 threads (recommended)
|
|
157
|
+
- **Storage**: Sufficient space for the GEO dataset
|
|
158
|
+
- **Operating System**: Linux/Unix recommended (Windows users may need additional configuration)
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Expose key classes and functions for easy access
|
|
2
|
+
|
|
3
|
+
from .citegeist_model import CitegeistModel
|
|
4
|
+
from .gurobi_impl import optimize_cell_proportions, optimize_gene_expression, map_antibodies_to_profiles
|
|
5
|
+
from .utils import (
|
|
6
|
+
save_results_to_output,
|
|
7
|
+
validate_cell_profile_dict,
|
|
8
|
+
cleanup_memory,
|
|
9
|
+
setup_logging
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"CitegeistModel",
|
|
14
|
+
"optimize_cell_proportions",
|
|
15
|
+
"optimize_gene_expression",
|
|
16
|
+
"map_antibodies_to_profiles",
|
|
17
|
+
"save_results_to_output",
|
|
18
|
+
"validate_cell_profile_dict",
|
|
19
|
+
"cleanup_memory",
|
|
20
|
+
"setup_logging"
|
|
21
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
import scanpy as sc
|
|
3
|
+
import anndata as ad
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def filter_spots_by_celltype_proportion(adata, celltype, proportion_threshold):
|
|
7
|
+
"""
|
|
8
|
+
Filter spots by celltype proportion based on if their .obs[celltype] is greater than the proportion_threshold.
|
|
9
|
+
"""
|
|
10
|
+
return adata[adata.obs[celltype] > proportion_threshold].copy()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def expand_prop_gex_adata(prop_gex_adata, celltype_cutoff=0.2, celltype_profile_dict=None):
|
|
14
|
+
"""
|
|
15
|
+
Expands celltype level spots to have multiple spots for each celltype, but retain spatial information
|
|
16
|
+
for COMMMOT analysis.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
if celltype_profile_dict is None:
|
|
20
|
+
raise ValueError("celltype_profile_dict is required for this function.")
|
|
21
|
+
celltypes = celltype_profile_dict.keys()
|
|
22
|
+
|
|
23
|
+
all_adatas = []
|
|
24
|
+
|
|
25
|
+
for celltype in celltypes:
|
|
26
|
+
filtered_adata = filter_spots_by_celltype_proportion(prop_gex_adata, celltype, celltype_cutoff)
|
|
27
|
+
celltype_label = celltype.replace(" ", "_")
|
|
28
|
+
layer_name = f"{celltype_label}_genes_pass1"
|
|
29
|
+
filtered_adata.X = filtered_adata.layers[layer_name]
|
|
30
|
+
filtered_adata.obs['celltype'] = celltype
|
|
31
|
+
all_adatas.append(filtered_adata)
|
|
32
|
+
# Extract the corresponding layer for the filtered_adata, including the spatial information
|
|
33
|
+
combined_adata = ad.concat(all_adatas, join="outer", keys=celltypes, index_unique = "_", merge="same", uns_merge = "first")
|
|
34
|
+
return combined_adata
|
|
35
|
+
|
|
36
|
+
def get_celltype_expression_data(adata, celltype):
|
|
37
|
+
"""
|
|
38
|
+
Get expression data for a specific celltype.
|
|
39
|
+
"""
|
|
40
|
+
return adata[adata.obs['celltype'] == celltype].copy()
|
|
41
|
+
|
|
42
|
+
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
class CheckpointManager:
|
|
7
|
+
"""Manages loading and saving of optimization checkpoints."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, output_dir, sample_name):
|
|
10
|
+
"""
|
|
11
|
+
Initialize checkpoint manager.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
output_dir (str): Directory for checkpoint files
|
|
15
|
+
sample_name (str): Unique identifier for this sample
|
|
16
|
+
"""
|
|
17
|
+
self.output_dir = Path(output_dir)
|
|
18
|
+
self.sample_name = sample_name
|
|
19
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
def check_complete_run(self, N, T, M):
|
|
22
|
+
"""
|
|
23
|
+
Check if a complete run exists.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
N (int): Number of spots
|
|
27
|
+
T (int): Number of cell types
|
|
28
|
+
M (int): Number of genes/features
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
dict or None: Dictionary of spot profiles if complete run exists, else None
|
|
32
|
+
"""
|
|
33
|
+
complete_file = self.output_dir / f"{self.sample_name}_gene_expression_complete.npz"
|
|
34
|
+
|
|
35
|
+
if complete_file.exists():
|
|
36
|
+
try:
|
|
37
|
+
complete_data = np.load(complete_file)
|
|
38
|
+
if 'profiles' in complete_data and 'completed_spots' in complete_data:
|
|
39
|
+
profiles = complete_data['profiles']
|
|
40
|
+
if profiles.shape == (N, T, M):
|
|
41
|
+
return {i: profiles[i] for i in range(N)}
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logging.error(f"Error loading complete file: {e}")
|
|
44
|
+
self._cleanup_corrupted_files()
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def load_latest_checkpoint(self, N, T, M):
|
|
48
|
+
"""
|
|
49
|
+
Load the latest valid checkpoint.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
N (int): Number of spots
|
|
53
|
+
T (int): Number of cell types
|
|
54
|
+
M (int): Number of genes/features
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
tuple: (completed_spots set, spotwise_profiles dict)
|
|
58
|
+
"""
|
|
59
|
+
checkpoints = list(self.output_dir.glob(
|
|
60
|
+
f"{self.sample_name}_gene_expression_checkpoint_*.npz"
|
|
61
|
+
))
|
|
62
|
+
|
|
63
|
+
if not checkpoints:
|
|
64
|
+
return set(), {}
|
|
65
|
+
|
|
66
|
+
checkpoint_numbers = [
|
|
67
|
+
int(f.stem.split('_')[-1])
|
|
68
|
+
for f in checkpoints
|
|
69
|
+
]
|
|
70
|
+
latest_number = max(checkpoint_numbers)
|
|
71
|
+
latest_checkpoint = self.output_dir / f"{self.sample_name}_gene_expression_checkpoint_{latest_number}.npz"
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
checkpoint_data = np.load(latest_checkpoint)
|
|
75
|
+
if 'profiles' in checkpoint_data and 'completed_spots' in checkpoint_data:
|
|
76
|
+
profiles = checkpoint_data['profiles']
|
|
77
|
+
completed_spots = set(checkpoint_data['completed_spots'].tolist())
|
|
78
|
+
|
|
79
|
+
if profiles.shape == (N, T, M):
|
|
80
|
+
spotwise_profiles = {
|
|
81
|
+
i: profiles[i]
|
|
82
|
+
for i in completed_spots
|
|
83
|
+
if not np.any(np.isnan(profiles[i]))
|
|
84
|
+
}
|
|
85
|
+
completed_spots = set(spotwise_profiles.keys())
|
|
86
|
+
logging.info(f"Loaded {len(completed_spots)} valid profiles from checkpoint")
|
|
87
|
+
return completed_spots, spotwise_profiles
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logging.error(f"Error loading checkpoint: {e}")
|
|
91
|
+
self._cleanup_corrupted_files()
|
|
92
|
+
|
|
93
|
+
return set(), {}
|
|
94
|
+
|
|
95
|
+
def save_checkpoint(self, completed_spots, spotwise_profiles, N, T, M):
|
|
96
|
+
"""
|
|
97
|
+
Save current progress as checkpoint.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
completed_spots (set): Set of completed spot indices
|
|
101
|
+
spotwise_profiles (dict): Dictionary of spot profiles
|
|
102
|
+
N (int): Number of spots
|
|
103
|
+
T (int): Number of cell types
|
|
104
|
+
M (int): Number of genes/features
|
|
105
|
+
"""
|
|
106
|
+
try:
|
|
107
|
+
n_completed = len(completed_spots)
|
|
108
|
+
checkpoint_path = self.output_dir / f"{self.sample_name}_gene_expression_checkpoint_{n_completed}.npz"
|
|
109
|
+
|
|
110
|
+
# Convert dictionary to numpy array
|
|
111
|
+
max_spot = max(spotwise_profiles.keys())
|
|
112
|
+
profiles_array = np.full((max_spot + 1, T, M), np.nan)
|
|
113
|
+
|
|
114
|
+
for spot_idx, profile in spotwise_profiles.items():
|
|
115
|
+
profiles_array[spot_idx] = profile
|
|
116
|
+
|
|
117
|
+
# Save checkpoint
|
|
118
|
+
np.savez_compressed(
|
|
119
|
+
checkpoint_path,
|
|
120
|
+
profiles=profiles_array,
|
|
121
|
+
completed_spots=np.array(list(completed_spots)),
|
|
122
|
+
n_completed=n_completed
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Cleanup old checkpoints
|
|
126
|
+
self._cleanup_old_checkpoints(checkpoint_path)
|
|
127
|
+
|
|
128
|
+
logging.info(f"Saved checkpoint after {n_completed} completed spots")
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logging.error(f"Failed to save checkpoint: {e}")
|
|
132
|
+
|
|
133
|
+
def save_final_results(self, spotwise_profiles, completed_spots, N, T, M):
|
|
134
|
+
"""
|
|
135
|
+
Save final results.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
spotwise_profiles (dict): Dictionary of spot profiles
|
|
139
|
+
completed_spots (set): Set of completed spot indices
|
|
140
|
+
N (int): Number of spots
|
|
141
|
+
T (int): Number of cell types
|
|
142
|
+
M (int): Number of genes/features
|
|
143
|
+
"""
|
|
144
|
+
final_path = self.output_dir / f"{self.sample_name}_gene_expression_complete.npz"
|
|
145
|
+
max_spot = max(spotwise_profiles.keys())
|
|
146
|
+
final_profiles = np.full((max_spot + 1, T, M), np.nan)
|
|
147
|
+
|
|
148
|
+
for spot_idx, profile in spotwise_profiles.items():
|
|
149
|
+
final_profiles[spot_idx] = profile
|
|
150
|
+
|
|
151
|
+
np.savez_compressed(
|
|
152
|
+
final_path,
|
|
153
|
+
profiles=final_profiles,
|
|
154
|
+
completed_spots=np.array(list(completed_spots))
|
|
155
|
+
)
|
|
156
|
+
logging.info(f"Saved final results with {len(completed_spots)} completed spots")
|
|
157
|
+
|
|
158
|
+
def _cleanup_corrupted_files(self):
|
|
159
|
+
"""Remove all checkpoint files if corruption is detected."""
|
|
160
|
+
for file in self.output_dir.glob(f"{self.sample_name}_gene_expression*.npz"):
|
|
161
|
+
try:
|
|
162
|
+
file.unlink()
|
|
163
|
+
logging.info(f"Deleted corrupted checkpoint: {file}")
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logging.warning(f"Failed to delete {file}: {e}")
|
|
166
|
+
|
|
167
|
+
def _cleanup_old_checkpoints(self, current_checkpoint):
|
|
168
|
+
"""Remove old checkpoints, keeping only the latest."""
|
|
169
|
+
for checkpoint in self.output_dir.glob(f"{self.sample_name}_gene_expression_checkpoint_*.npz"):
|
|
170
|
+
if checkpoint != current_checkpoint:
|
|
171
|
+
try:
|
|
172
|
+
checkpoint.unlink()
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logging.warning(f"Failed to delete old checkpoint {checkpoint}: {e}")
|