CATranS 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.
- catrans-0.1.0/.gitignore +9 -0
- catrans-0.1.0/LICENSE +21 -0
- catrans-0.1.0/PKG-INFO +268 -0
- catrans-0.1.0/README.md +241 -0
- catrans-0.1.0/pyproject.toml +67 -0
- catrans-0.1.0/src/catrans/__init__.py +14 -0
- catrans-0.1.0/src/catrans/_logging.py +38 -0
- catrans-0.1.0/src/catrans/core.py +575 -0
- catrans-0.1.0/src/catrans/data.py +61 -0
- catrans-0.1.0/tests/test_catrans.py +220 -0
catrans-0.1.0/.gitignore
ADDED
catrans-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ruben-canas-ivi
|
|
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.
|
catrans-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: CATranS
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clinical Acute Transcriptomic Stratification
|
|
5
|
+
Project-URL: Homepage, https://github.com/ruben-canas-ivi/CATranS
|
|
6
|
+
Project-URL: Issues, https://github.com/ruben-canas-ivi/CATranS/issues
|
|
7
|
+
Author-email: Patricia Sebastián-León <patricia.sebastian@ivirma.com>, Antonio Parraga-Leo <antonio.parraga@ivirma.com>, Rubén Cañas-Cañas <ruben.canas@ivirma.com>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: bioinformatics,deseq2,genomics,machine-learning,stratification
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: igraph>=1.0.0
|
|
20
|
+
Requires-Dist: inmoose>=0.9.1
|
|
21
|
+
Requires-Dist: numpy>=2.3.0
|
|
22
|
+
Requires-Dist: pandas>=2.3.3
|
|
23
|
+
Requires-Dist: pydeseq2>=0.5.4
|
|
24
|
+
Requires-Dist: scikit-learn>=1.8.0
|
|
25
|
+
Requires-Dist: scipy>=1.17.1
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# CATranS: Clinical Acute Transcriptomic Stratification
|
|
29
|
+
|
|
30
|
+
**CATranS** is a robust bioinformatics and machine learning pipeline written in Python designed to stratify acute clinical cohorts using transcriptomic count data.
|
|
31
|
+
|
|
32
|
+
Through an iterative, semi-supervised learning approach, CATranS combines data normalization, batch correction, correlation-based feature selection, and class prediction to refine classifications of patient cohorts (e.g., separating acute from non-acute states, or predicting survival outcomes).
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Key Features
|
|
37
|
+
|
|
38
|
+
- **Iterative Semi-Supervised Relabeling**: Safely expands training cohorts by predicting clinical labels on unclassified/unlabeled samples using high-confidence thresholds.
|
|
39
|
+
- **Generalized Custom Classes**: Supports user-defined positive and negative class labeling (e.g., `Survivor`/`NonSurvivor`, `Mild`/`Severe`, `Responders`/`NonResponders`) with robust Categorical ordering to fix Wald contrast directions in DESeq2.
|
|
40
|
+
- **Batch Correction**: Integrated with `inmoose` for ComBat-seq count batch effect correction.
|
|
41
|
+
- **DESeq2-style Normalization**: Leverages the high-performance `pydeseq2` implementation for median-of-ratios normalization.
|
|
42
|
+
- **Correlation-Based Feature Learning**: Custom graph-theory based gene selection utilizing `igraph`. Filters out highly redundant co-expressed genes and selects optimal representative biomarkers correlating with clinical outcomes.
|
|
43
|
+
- **Flexible Estimator Integration**: Compatible with any Scikit-Learn classifier exposing a `predict_proba` method.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Workflow Overview
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
graph TD
|
|
51
|
+
A[Raw Count Matrix & Metadata] --> B[Data Normalization & Optional ComBat-seq Batch Correction]
|
|
52
|
+
B --> C[Feature Selection: Spearman Correlation with Labels]
|
|
53
|
+
C --> D[Graph Construction: Pearson Gene-Gene Network]
|
|
54
|
+
D --> E[Redundant Feature Compression: Connected Components Vertex Selection]
|
|
55
|
+
E --> F[Iterative Semi-Supervised Model Training]
|
|
56
|
+
F --> G[Fit Classifier & Predict Unlabeled Samples]
|
|
57
|
+
G --> H{Meets Threshold?}
|
|
58
|
+
H -- Yes --> I[Relabel Samples & Run DESeq2 Differential Expression Analysis]
|
|
59
|
+
I --> J{Are there new DEGs?}
|
|
60
|
+
J -- Yes --> F
|
|
61
|
+
J -- No --> K[Final Output Classifications & Iteration History]
|
|
62
|
+
H -- No --> K
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
We recommend using [uv](https://github.com/astral-sh/uv) or `pip` for rapid virtual environment setup and dependency management.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Clone the repository
|
|
73
|
+
git clone https://github.com/ruben-canas-ivi/CATranS.git
|
|
74
|
+
cd CATranS
|
|
75
|
+
|
|
76
|
+
# Install dependencies and the package using uv
|
|
77
|
+
uv sync
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Dependencies
|
|
81
|
+
- Python `>=3.11`
|
|
82
|
+
- `numpy>=2.3.0`
|
|
83
|
+
- `pandas>=2.3.3`
|
|
84
|
+
- `scipy>=1.14.0`
|
|
85
|
+
- `scikit-learn>=1.8.0`
|
|
86
|
+
- `pydeseq2>=0.5.4`
|
|
87
|
+
- `igraph>=1.0.0`
|
|
88
|
+
- `inmoose>=0.9.1`
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## API Reference & Usage Scenarios
|
|
93
|
+
|
|
94
|
+
### Core Classes & Functions
|
|
95
|
+
|
|
96
|
+
#### 1. `CatransClasses`
|
|
97
|
+
Defines the names of the binary classes.
|
|
98
|
+
- `positive` (default `"Dead"`): The target/case class.
|
|
99
|
+
- `negative` (default `"Alive"`): The reference/control class.
|
|
100
|
+
|
|
101
|
+
#### 2. `correlation_based_feature_selection`
|
|
102
|
+
Identifies representative biomarker genes by correlating expression levels with labels, building a gene-gene correlation network, and selecting one representative gene per connected component.
|
|
103
|
+
|
|
104
|
+
#### 3. `run_catrans`
|
|
105
|
+
Runs the iterative semi-supervised relabeling pipeline.
|
|
106
|
+
- `raw_counts` (pd.DataFrame): Genes (rows) by Samples (columns).
|
|
107
|
+
- `metadata` (pd.DataFrame): Samples (rows) with metadata.
|
|
108
|
+
- `acute_classification` (pd.DataFrame): Samples (rows) with initially known labels under the column `"class"` (unlabeled samples should be `NaN`).
|
|
109
|
+
- `model`: Any Scikit-Learn compatible classifier (e.g. `RandomForestClassifier`, `SVC(probability=True)`).
|
|
110
|
+
- `classes` (CatransClasses, Optional): Custom positive/negative class names.
|
|
111
|
+
- `batch_col` (str, Optional): Metadata column to use for ComBat-seq batch correction.
|
|
112
|
+
- `model_threshold` (float, default `0.8`): Confidence threshold to assign a label.
|
|
113
|
+
- `signature_genes` (list[str], Optional): Genes to use for training the model. If `None`, `correlation_based_feature_selection` runs automatically.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### Usage Scenarios
|
|
118
|
+
|
|
119
|
+
#### Scenario 1: Normal Workflow (Two-step Feature Selection + Stratification)
|
|
120
|
+
This is the recommended workflow where you first find a non-redundant gene signature, then pass it to the CATranS pipeline.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import pandas as pd
|
|
124
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
125
|
+
import catrans
|
|
126
|
+
|
|
127
|
+
# 1. Load your transcriptomics and clinical datasets
|
|
128
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
129
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
130
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
131
|
+
|
|
132
|
+
# 2. Define custom labels: "Survivor" (negative) and "NonSurvivor" (positive)
|
|
133
|
+
my_classes = catrans.CatransClasses(positive="NonSurvivor", negative="Survivor")
|
|
134
|
+
|
|
135
|
+
# 3. Perform correlation-based feature selection to identify candidate biomarkers
|
|
136
|
+
signature = catrans.correlation_based_feature_selection(
|
|
137
|
+
data=counts,
|
|
138
|
+
acute_classification=initial_classes,
|
|
139
|
+
classes=my_classes,
|
|
140
|
+
pval=0.05,
|
|
141
|
+
cutoff_corr=0.5,
|
|
142
|
+
cutoff_corr_genes=0.8,
|
|
143
|
+
n_cpus=4
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# 4. Run the semi-supervised relabeling pipeline using the selected signature
|
|
147
|
+
results = catrans.run_catrans(
|
|
148
|
+
raw_counts=counts,
|
|
149
|
+
metadata=metadata,
|
|
150
|
+
acute_classification=initial_classes,
|
|
151
|
+
classes=my_classes,
|
|
152
|
+
model=RandomForestClassifier(random_state=42),
|
|
153
|
+
model_threshold=0.75,
|
|
154
|
+
signature_genes=signature,
|
|
155
|
+
n_cpus=4
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
print(results.final_classification)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Scenario 2: Predefined/Custom Gene Signature (Direct Stratification)
|
|
162
|
+
If you already have a target gene signature (e.g., from literature or a previous experiment), you can bypass the feature selection step entirely and run the stratification pipeline directly.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
import pandas as pd
|
|
166
|
+
from sklearn.svm import SVC
|
|
167
|
+
import catrans
|
|
168
|
+
|
|
169
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
170
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
171
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
172
|
+
|
|
173
|
+
# Custom predefined signature from literature
|
|
174
|
+
custom_signature = ["ENSG00000034693.15", "ENSG00000101188.5", "ENSG00000104870.13"]
|
|
175
|
+
|
|
176
|
+
# Run the CATranS pipeline directly with the custom signature
|
|
177
|
+
results = catrans.run_catrans(
|
|
178
|
+
raw_counts=counts,
|
|
179
|
+
metadata=metadata,
|
|
180
|
+
acute_classification=initial_classes,
|
|
181
|
+
model=SVC(probability=True, random_state=42),
|
|
182
|
+
model_threshold=0.8,
|
|
183
|
+
signature_genes=custom_signature,
|
|
184
|
+
n_cpus=4
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
print(f"Completed in {results.number_of_iterations} iterations.")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### Scenario 3: Implicit Feature Selection (Defaulting Signature Genes)
|
|
191
|
+
If you do not specify a list of `signature_genes`, `run_catrans` will automatically execute `correlation_based_feature_selection` under the hood using default parameters.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
import pandas as pd
|
|
195
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
196
|
+
import catrans
|
|
197
|
+
|
|
198
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
199
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
200
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
201
|
+
|
|
202
|
+
# Run CATranS (it will automatically perform feature selection internally)
|
|
203
|
+
results = catrans.run_catrans(
|
|
204
|
+
raw_counts=counts,
|
|
205
|
+
metadata=metadata,
|
|
206
|
+
acute_classification=initial_classes,
|
|
207
|
+
model=RandomForestClassifier(random_state=42),
|
|
208
|
+
model_threshold=0.75,
|
|
209
|
+
n_cpus=4
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Scenario 4: Default Thresholds & Core Settings
|
|
214
|
+
You can omit optional arguments like `model_threshold` or `classes` if you wish to use the defaults (`model_threshold=0.8`, `classes` defaulting to `"Dead"`/`"Alive"`).
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import pandas as pd
|
|
218
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
219
|
+
import catrans
|
|
220
|
+
|
|
221
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
222
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
223
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
224
|
+
|
|
225
|
+
# Minimal parameters
|
|
226
|
+
results = catrans.run_catrans(
|
|
227
|
+
raw_counts=counts,
|
|
228
|
+
metadata=metadata,
|
|
229
|
+
acute_classification=initial_classes,
|
|
230
|
+
model=RandomForestClassifier(random_state=42)
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### Scenario 5: Running with Batch Correction
|
|
235
|
+
To normalize and batch-correct raw counts concurrently, specify the name of the batch column in your metadata dataframe:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
results = catrans.run_catrans(
|
|
239
|
+
raw_counts=counts,
|
|
240
|
+
metadata=metadata,
|
|
241
|
+
acute_classification=initial_classes,
|
|
242
|
+
model=RandomForestClassifier(random_state=42),
|
|
243
|
+
batch_col="batch" # ComBat-seq batch effect correction column
|
|
244
|
+
)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Testing & Quality Assurance
|
|
250
|
+
|
|
251
|
+
CATranS utilizes `pytest` for automated test suites, `ruff` for code style consistency, and `mypy` for strict type checking.
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Run tests
|
|
255
|
+
uv run pytest
|
|
256
|
+
|
|
257
|
+
# Check code format and style
|
|
258
|
+
uv run ruff check .
|
|
259
|
+
|
|
260
|
+
# Perform static type checks
|
|
261
|
+
uv run mypy src/catrans
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
catrans-0.1.0/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# CATranS: Clinical Acute Transcriptomic Stratification
|
|
2
|
+
|
|
3
|
+
**CATranS** is a robust bioinformatics and machine learning pipeline written in Python designed to stratify acute clinical cohorts using transcriptomic count data.
|
|
4
|
+
|
|
5
|
+
Through an iterative, semi-supervised learning approach, CATranS combines data normalization, batch correction, correlation-based feature selection, and class prediction to refine classifications of patient cohorts (e.g., separating acute from non-acute states, or predicting survival outcomes).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Iterative Semi-Supervised Relabeling**: Safely expands training cohorts by predicting clinical labels on unclassified/unlabeled samples using high-confidence thresholds.
|
|
12
|
+
- **Generalized Custom Classes**: Supports user-defined positive and negative class labeling (e.g., `Survivor`/`NonSurvivor`, `Mild`/`Severe`, `Responders`/`NonResponders`) with robust Categorical ordering to fix Wald contrast directions in DESeq2.
|
|
13
|
+
- **Batch Correction**: Integrated with `inmoose` for ComBat-seq count batch effect correction.
|
|
14
|
+
- **DESeq2-style Normalization**: Leverages the high-performance `pydeseq2` implementation for median-of-ratios normalization.
|
|
15
|
+
- **Correlation-Based Feature Learning**: Custom graph-theory based gene selection utilizing `igraph`. Filters out highly redundant co-expressed genes and selects optimal representative biomarkers correlating with clinical outcomes.
|
|
16
|
+
- **Flexible Estimator Integration**: Compatible with any Scikit-Learn classifier exposing a `predict_proba` method.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Workflow Overview
|
|
21
|
+
|
|
22
|
+
```mermaid
|
|
23
|
+
graph TD
|
|
24
|
+
A[Raw Count Matrix & Metadata] --> B[Data Normalization & Optional ComBat-seq Batch Correction]
|
|
25
|
+
B --> C[Feature Selection: Spearman Correlation with Labels]
|
|
26
|
+
C --> D[Graph Construction: Pearson Gene-Gene Network]
|
|
27
|
+
D --> E[Redundant Feature Compression: Connected Components Vertex Selection]
|
|
28
|
+
E --> F[Iterative Semi-Supervised Model Training]
|
|
29
|
+
F --> G[Fit Classifier & Predict Unlabeled Samples]
|
|
30
|
+
G --> H{Meets Threshold?}
|
|
31
|
+
H -- Yes --> I[Relabel Samples & Run DESeq2 Differential Expression Analysis]
|
|
32
|
+
I --> J{Are there new DEGs?}
|
|
33
|
+
J -- Yes --> F
|
|
34
|
+
J -- No --> K[Final Output Classifications & Iteration History]
|
|
35
|
+
H -- No --> K
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
We recommend using [uv](https://github.com/astral-sh/uv) or `pip` for rapid virtual environment setup and dependency management.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Clone the repository
|
|
46
|
+
git clone https://github.com/ruben-canas-ivi/CATranS.git
|
|
47
|
+
cd CATranS
|
|
48
|
+
|
|
49
|
+
# Install dependencies and the package using uv
|
|
50
|
+
uv sync
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Dependencies
|
|
54
|
+
- Python `>=3.11`
|
|
55
|
+
- `numpy>=2.3.0`
|
|
56
|
+
- `pandas>=2.3.3`
|
|
57
|
+
- `scipy>=1.14.0`
|
|
58
|
+
- `scikit-learn>=1.8.0`
|
|
59
|
+
- `pydeseq2>=0.5.4`
|
|
60
|
+
- `igraph>=1.0.0`
|
|
61
|
+
- `inmoose>=0.9.1`
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## API Reference & Usage Scenarios
|
|
66
|
+
|
|
67
|
+
### Core Classes & Functions
|
|
68
|
+
|
|
69
|
+
#### 1. `CatransClasses`
|
|
70
|
+
Defines the names of the binary classes.
|
|
71
|
+
- `positive` (default `"Dead"`): The target/case class.
|
|
72
|
+
- `negative` (default `"Alive"`): The reference/control class.
|
|
73
|
+
|
|
74
|
+
#### 2. `correlation_based_feature_selection`
|
|
75
|
+
Identifies representative biomarker genes by correlating expression levels with labels, building a gene-gene correlation network, and selecting one representative gene per connected component.
|
|
76
|
+
|
|
77
|
+
#### 3. `run_catrans`
|
|
78
|
+
Runs the iterative semi-supervised relabeling pipeline.
|
|
79
|
+
- `raw_counts` (pd.DataFrame): Genes (rows) by Samples (columns).
|
|
80
|
+
- `metadata` (pd.DataFrame): Samples (rows) with metadata.
|
|
81
|
+
- `acute_classification` (pd.DataFrame): Samples (rows) with initially known labels under the column `"class"` (unlabeled samples should be `NaN`).
|
|
82
|
+
- `model`: Any Scikit-Learn compatible classifier (e.g. `RandomForestClassifier`, `SVC(probability=True)`).
|
|
83
|
+
- `classes` (CatransClasses, Optional): Custom positive/negative class names.
|
|
84
|
+
- `batch_col` (str, Optional): Metadata column to use for ComBat-seq batch correction.
|
|
85
|
+
- `model_threshold` (float, default `0.8`): Confidence threshold to assign a label.
|
|
86
|
+
- `signature_genes` (list[str], Optional): Genes to use for training the model. If `None`, `correlation_based_feature_selection` runs automatically.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Usage Scenarios
|
|
91
|
+
|
|
92
|
+
#### Scenario 1: Normal Workflow (Two-step Feature Selection + Stratification)
|
|
93
|
+
This is the recommended workflow where you first find a non-redundant gene signature, then pass it to the CATranS pipeline.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import pandas as pd
|
|
97
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
98
|
+
import catrans
|
|
99
|
+
|
|
100
|
+
# 1. Load your transcriptomics and clinical datasets
|
|
101
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
102
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
103
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
104
|
+
|
|
105
|
+
# 2. Define custom labels: "Survivor" (negative) and "NonSurvivor" (positive)
|
|
106
|
+
my_classes = catrans.CatransClasses(positive="NonSurvivor", negative="Survivor")
|
|
107
|
+
|
|
108
|
+
# 3. Perform correlation-based feature selection to identify candidate biomarkers
|
|
109
|
+
signature = catrans.correlation_based_feature_selection(
|
|
110
|
+
data=counts,
|
|
111
|
+
acute_classification=initial_classes,
|
|
112
|
+
classes=my_classes,
|
|
113
|
+
pval=0.05,
|
|
114
|
+
cutoff_corr=0.5,
|
|
115
|
+
cutoff_corr_genes=0.8,
|
|
116
|
+
n_cpus=4
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# 4. Run the semi-supervised relabeling pipeline using the selected signature
|
|
120
|
+
results = catrans.run_catrans(
|
|
121
|
+
raw_counts=counts,
|
|
122
|
+
metadata=metadata,
|
|
123
|
+
acute_classification=initial_classes,
|
|
124
|
+
classes=my_classes,
|
|
125
|
+
model=RandomForestClassifier(random_state=42),
|
|
126
|
+
model_threshold=0.75,
|
|
127
|
+
signature_genes=signature,
|
|
128
|
+
n_cpus=4
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
print(results.final_classification)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Scenario 2: Predefined/Custom Gene Signature (Direct Stratification)
|
|
135
|
+
If you already have a target gene signature (e.g., from literature or a previous experiment), you can bypass the feature selection step entirely and run the stratification pipeline directly.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import pandas as pd
|
|
139
|
+
from sklearn.svm import SVC
|
|
140
|
+
import catrans
|
|
141
|
+
|
|
142
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
143
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
144
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
145
|
+
|
|
146
|
+
# Custom predefined signature from literature
|
|
147
|
+
custom_signature = ["ENSG00000034693.15", "ENSG00000101188.5", "ENSG00000104870.13"]
|
|
148
|
+
|
|
149
|
+
# Run the CATranS pipeline directly with the custom signature
|
|
150
|
+
results = catrans.run_catrans(
|
|
151
|
+
raw_counts=counts,
|
|
152
|
+
metadata=metadata,
|
|
153
|
+
acute_classification=initial_classes,
|
|
154
|
+
model=SVC(probability=True, random_state=42),
|
|
155
|
+
model_threshold=0.8,
|
|
156
|
+
signature_genes=custom_signature,
|
|
157
|
+
n_cpus=4
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
print(f"Completed in {results.number_of_iterations} iterations.")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Scenario 3: Implicit Feature Selection (Defaulting Signature Genes)
|
|
164
|
+
If you do not specify a list of `signature_genes`, `run_catrans` will automatically execute `correlation_based_feature_selection` under the hood using default parameters.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import pandas as pd
|
|
168
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
169
|
+
import catrans
|
|
170
|
+
|
|
171
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
172
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
173
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
174
|
+
|
|
175
|
+
# Run CATranS (it will automatically perform feature selection internally)
|
|
176
|
+
results = catrans.run_catrans(
|
|
177
|
+
raw_counts=counts,
|
|
178
|
+
metadata=metadata,
|
|
179
|
+
acute_classification=initial_classes,
|
|
180
|
+
model=RandomForestClassifier(random_state=42),
|
|
181
|
+
model_threshold=0.75,
|
|
182
|
+
n_cpus=4
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Scenario 4: Default Thresholds & Core Settings
|
|
187
|
+
You can omit optional arguments like `model_threshold` or `classes` if you wish to use the defaults (`model_threshold=0.8`, `classes` defaulting to `"Dead"`/`"Alive"`).
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
import pandas as pd
|
|
191
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
192
|
+
import catrans
|
|
193
|
+
|
|
194
|
+
counts = pd.read_csv("data/raw_counts.tsv", sep="\t", index_col=0)
|
|
195
|
+
metadata = pd.read_csv("data/metadata.tsv", sep="\t", index_col=0)
|
|
196
|
+
initial_classes = pd.read_csv("data/initial_classes.tsv", sep="\t", index_col=0)
|
|
197
|
+
|
|
198
|
+
# Minimal parameters
|
|
199
|
+
results = catrans.run_catrans(
|
|
200
|
+
raw_counts=counts,
|
|
201
|
+
metadata=metadata,
|
|
202
|
+
acute_classification=initial_classes,
|
|
203
|
+
model=RandomForestClassifier(random_state=42)
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Scenario 5: Running with Batch Correction
|
|
208
|
+
To normalize and batch-correct raw counts concurrently, specify the name of the batch column in your metadata dataframe:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
results = catrans.run_catrans(
|
|
212
|
+
raw_counts=counts,
|
|
213
|
+
metadata=metadata,
|
|
214
|
+
acute_classification=initial_classes,
|
|
215
|
+
model=RandomForestClassifier(random_state=42),
|
|
216
|
+
batch_col="batch" # ComBat-seq batch effect correction column
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Testing & Quality Assurance
|
|
223
|
+
|
|
224
|
+
CATranS utilizes `pytest` for automated test suites, `ruff` for code style consistency, and `mypy` for strict type checking.
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Run tests
|
|
228
|
+
uv run pytest
|
|
229
|
+
|
|
230
|
+
# Check code format and style
|
|
231
|
+
uv run ruff check .
|
|
232
|
+
|
|
233
|
+
# Perform static type checks
|
|
234
|
+
uv run mypy src/catrans
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "CATranS"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Clinical Acute Transcriptomic Stratification"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Patricia Sebastián-León", email = "patricia.sebastian@ivirma.com" },
|
|
7
|
+
{ name = "Antonio Parraga-Leo", email = "antonio.parraga@ivirma.com" },
|
|
8
|
+
{ name = "Rubén Cañas-Cañas", email = "ruben.canas@ivirma.com" }
|
|
9
|
+
]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
keywords = ["bioinformatics", "machine-learning", "deseq2", "stratification", "genomics"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Science/Research",
|
|
16
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
dependencies = [
|
|
25
|
+
"numpy>=2.3.0",
|
|
26
|
+
"pandas>=2.3.3",
|
|
27
|
+
"scikit-learn>=1.8.0",
|
|
28
|
+
"scipy>=1.17.1",
|
|
29
|
+
"pydeseq2>=0.5.4",
|
|
30
|
+
"igraph>=1.0.0",
|
|
31
|
+
"inmoose>=0.9.1",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/ruben-canas-ivi/CATranS"
|
|
36
|
+
Issues = "https://github.com/ruben-canas-ivi/CATranS/issues"
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["hatchling"]
|
|
40
|
+
build-backend = "hatchling.build"
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=8.0.0",
|
|
45
|
+
"ruff>=0.9.0",
|
|
46
|
+
"mypy>=1.14.0",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
line-length = 120
|
|
51
|
+
target-version = "py311"
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
python_version = "3.11"
|
|
55
|
+
strict = true
|
|
56
|
+
|
|
57
|
+
[[tool.mypy.overrides]]
|
|
58
|
+
module = [
|
|
59
|
+
"pydeseq2.*",
|
|
60
|
+
"igraph.*",
|
|
61
|
+
"inmoose.*",
|
|
62
|
+
"statsmodels.*",
|
|
63
|
+
"scipy.*",
|
|
64
|
+
"pandas.*"
|
|
65
|
+
]
|
|
66
|
+
ignore_missing_imports = true
|
|
67
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from catrans.core import (
|
|
2
|
+
correlation_based_feature_selection,
|
|
3
|
+
run_catrans,
|
|
4
|
+
_diffExpAnalysis # recordar quitar esto antes de subir el paquete, que esto es solo para validaciones #TODO
|
|
5
|
+
)
|
|
6
|
+
from catrans.data import CatransClasses, CatransResult
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'correlation_based_feature_selection',
|
|
10
|
+
'run_catrans',
|
|
11
|
+
'_diffExpAnalysis', # recordar quitar esto antes de subir el paquete, que esto es solo para validaciones #TODO
|
|
12
|
+
'CatransClasses',
|
|
13
|
+
'CatransResult',
|
|
14
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
5
|
+
logger.propagate = False
|
|
6
|
+
|
|
7
|
+
class CustomFormatter(logging.Formatter):
|
|
8
|
+
info_format = "[%(asctime)s] | %(levelname)-8s | %(message)s"
|
|
9
|
+
debug_format = "[%(asctime)s] | %(levelname)-8s | STEP: %(funcName)s | %(message)s"
|
|
10
|
+
|
|
11
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
12
|
+
orig_levelname = record.levelname
|
|
13
|
+
record.levelname = orig_levelname.center(8)
|
|
14
|
+
|
|
15
|
+
if record.levelno == logging.DEBUG:
|
|
16
|
+
self._style._fmt = self.debug_format
|
|
17
|
+
else:
|
|
18
|
+
self._style._fmt = self.info_format
|
|
19
|
+
return super().format(record)
|
|
20
|
+
|
|
21
|
+
def _get_logger() -> logging.Logger:
|
|
22
|
+
return logging.getLogger(name=__name__)
|
|
23
|
+
|
|
24
|
+
def _setup_internal_logger(verbose: bool) -> None:
|
|
25
|
+
# Si no hay handlers, creamos uno con el CustomFormatter
|
|
26
|
+
if not logger.handlers:
|
|
27
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
28
|
+
handler.setFormatter(CustomFormatter(datefmt='%y-%m-%d %H:%M:%S'))
|
|
29
|
+
logger.addHandler(handler)
|
|
30
|
+
|
|
31
|
+
# Si el usuario activa verbose, bajamos a INFO
|
|
32
|
+
# Si tú desde fuera pusiste DEBUG, esto no lo sobrescribe (porque DEBUG < INFO)
|
|
33
|
+
if verbose:
|
|
34
|
+
# if logger.level > logging.INFO or logger.level == logging.NOTSET:
|
|
35
|
+
logger.setLevel(logging.INFO)
|
|
36
|
+
else:
|
|
37
|
+
if logger.level == logging.NOTSET or logger.level == logging.INFO:
|
|
38
|
+
logger.setLevel(logging.WARNING)
|