flexynesis 0.2.3__tar.gz → 0.2.5__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.
- {flexynesis-0.2.3 → flexynesis-0.2.5}/PKG-INFO +2 -2
- {flexynesis-0.2.3 → flexynesis-0.2.5}/README.md +1 -1
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/__main__.py +80 -45
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/data.py +29 -18
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/utils.py +116 -44
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis.egg-info/PKG-INFO +2 -2
- {flexynesis-0.2.3 → flexynesis-0.2.5}/pyproject.toml +1 -1
- {flexynesis-0.2.3 → flexynesis-0.2.5}/LICENCE.md +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/__init__.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/cli.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/config.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/feature_selection.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/main.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/__init__.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/crossmodal_pred.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/direct_pred.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/gnn_early.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/on_ice/direct_pred_cnn.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/on_ice/direct_pred_gcnn.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/on_ice/modules_on_ice.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/supervised_vae.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/models/triplet_encoder.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis/modules.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis.egg-info/SOURCES.txt +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis.egg-info/dependency_links.txt +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis.egg-info/entry_points.txt +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis.egg-info/requires.txt +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/flexynesis.egg-info/top_level.txt +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/setup.cfg +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/tests/__init__.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/tests/unit/__init__.py +0 -0
- {flexynesis-0.2.3 → flexynesis-0.2.5}/tests/unit/test_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flexynesis
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: A deep-learning based multi-omics bulk sequencing data integration suite with a focus on (pre-)clinical endpoint prediction.
|
|
5
5
|
Author-email: Bora Uyar <bora.uyar@mdc-berlin.de>, Taras Savchyn <Taras.Savchyn@mdc-berlin.de>, Ricardo Wurmus <Ricardo.Wurmus@mdc-berlin.de>, Ahmet Sarigun <Ahmet.Sariguen@mdc-berlin.de>
|
|
6
6
|
Project-URL: homepage, https://github.com/BIMSBbioinfo/flexynesis
|
|
@@ -113,7 +113,7 @@ flexynesis --data_path dataset1 \
|
|
|
113
113
|
--prefix erlotinib_direct \
|
|
114
114
|
--early_stop_patience 3 \
|
|
115
115
|
--use_loss_weighting False \
|
|
116
|
-
--evaluate_baseline_performance
|
|
116
|
+
--evaluate_baseline_performance
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
## Accelerating with GPUs
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from lightning import seed_everything
|
|
2
2
|
import lightning as pl
|
|
3
3
|
from typing import NamedTuple
|
|
4
|
-
import os, yaml, torch, time, random, warnings, argparse
|
|
4
|
+
import os, yaml, torch, time, random, warnings, argparse, sys
|
|
5
5
|
os.environ["OMP_NUM_THREADS"] = "1"
|
|
6
6
|
import pandas as pd
|
|
7
7
|
import flexynesis
|
|
@@ -18,7 +18,7 @@ def main():
|
|
|
18
18
|
|
|
19
19
|
Args:
|
|
20
20
|
--data_path (str): Path to the folder with train/test data files. (Required)
|
|
21
|
-
--model_class (str): The kind of model class to instantiate. Choices are ["DirectPred", "GNN", "supervised_vae", "MultiTripletNetwork", "CrossModalPred"]. (Required)
|
|
21
|
+
--model_class (str): The kind of model class to instantiate. Choices are ["DirectPred", "GNN", "supervised_vae", "MultiTripletNetwork", "CrossModalPred", "RandomForest", "SVM", "RandomSurvivalForest"]. (Required)
|
|
22
22
|
--gnn_conv_type (str): If model_class is set to GNN, choose which graph convolution type to use. Choices are ["GC", "GCN", "SAGE"].
|
|
23
23
|
--target_variables (str): Which variables in 'clin.csv' to use for predictions, comma-separated if multiple. Optional if survival variables are not set to None.
|
|
24
24
|
--batch_variables (str): Which variables in 'clin.csv' to use for data integration/batch correction, comma-separated if multiple. Optional.
|
|
@@ -44,7 +44,7 @@ def main():
|
|
|
44
44
|
--hpo_patience (int): How many hyperparameter optimisation iterations to wait for when no improvements are observed. Default is 10; set to 0 to disable early stopping.
|
|
45
45
|
--use_cv (bool): If set, a 5-fold cross-validation training will be done. Otherwise, a single training on 80 percent of the dataset is done.
|
|
46
46
|
--use_loss_weighting (str): Whether to apply loss-balancing using uncertainty weights method. Choices are ['True', 'False']. Default is 'True'.
|
|
47
|
-
--evaluate_baseline_performance (
|
|
47
|
+
--evaluate_baseline_performance (bool): Enables modeling also with Random Forest + SVMs to see the performance of off-the-shelf tools on the same dataset.
|
|
48
48
|
--threads (int): How many threads to use when using CPU. Default is 4.
|
|
49
49
|
--num_workers (int): How many workers to use for model training. Default is 2
|
|
50
50
|
--use_gpu (bool): If set, the system will attempt to use CUDA/GPU if available.
|
|
@@ -57,7 +57,7 @@ def main():
|
|
|
57
57
|
|
|
58
58
|
parser.add_argument("--data_path", help="(Required) Path to the folder with train/test data files", type=str, required = True)
|
|
59
59
|
parser.add_argument("--model_class", help="(Required) The kind of model class to instantiate", type=str,
|
|
60
|
-
choices=["DirectPred", "supervised_vae", "MultiTripletNetwork", "CrossModalPred", "GNN"], required = True)
|
|
60
|
+
choices=["DirectPred", "supervised_vae", "MultiTripletNetwork", "CrossModalPred", "GNN", "RandomForest", "SVM", "RandomSurvivalForest"], required = True)
|
|
61
61
|
parser.add_argument("--gnn_conv_type", help="If model_class is set to GNN, choose which graph convolution type to use", type=str,
|
|
62
62
|
choices=["GC", "GCN", "SAGE"])
|
|
63
63
|
parser.add_argument("--target_variables",
|
|
@@ -98,7 +98,8 @@ def main():
|
|
|
98
98
|
parser.add_argument("--use_cv", action="store_true",
|
|
99
99
|
help="(Optional) If set, the a 5-fold cross-validation training will be done. Otherwise, a single trainig on 80 percent of the dataset is done.")
|
|
100
100
|
parser.add_argument("--use_loss_weighting", help="whether to apply loss-balancing using uncertainty weights method", type=str, choices=['True', 'False'], default = 'True')
|
|
101
|
-
parser.add_argument("--evaluate_baseline_performance",
|
|
101
|
+
parser.add_argument("--evaluate_baseline_performance", action="store_true",
|
|
102
|
+
help="whether to run Random Forest + SVMs to see the performance of off-the-shelf tools on the same dataset")
|
|
102
103
|
parser.add_argument("--threads", help="(Optional) How many threads to use when using CPU (default is 4)", type=int, default = 4)
|
|
103
104
|
parser.add_argument("--num_workers", help="(Optional) How many workers to use for model training (default is 2)", type=int, default = 2)
|
|
104
105
|
parser.add_argument("--use_gpu", action="store_true",
|
|
@@ -140,12 +141,10 @@ def main():
|
|
|
140
141
|
"`srun --gpus=1 --pty flexynesis <rest of your_command>` !!!\n\n"]))
|
|
141
142
|
time.sleep(3) #wait a bit to capture user's attention to the warning
|
|
142
143
|
device_type = 'cpu'
|
|
143
|
-
torch.set_num_threads(args.threads)
|
|
144
144
|
else:
|
|
145
145
|
device_type = 'gpu'
|
|
146
146
|
else:
|
|
147
147
|
device_type = 'cpu'
|
|
148
|
-
torch.set_num_threads(args.threads)
|
|
149
148
|
|
|
150
149
|
# 5. check GNN arguments
|
|
151
150
|
if args.model_class == 'GNN':
|
|
@@ -186,20 +185,24 @@ def main():
|
|
|
186
185
|
if not os.path.exists(args.outdir):
|
|
187
186
|
raise FileNotFoundError(f"Path to --outdir doesn't exist at:", {args.outdir})
|
|
188
187
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
188
|
+
available_models = {
|
|
189
|
+
"DirectPred": (DirectPred, "DirectPred"),
|
|
190
|
+
"supervised_vae": (supervised_vae, "supervised_vae"),
|
|
191
|
+
"MultiTripletNetwork": (MultiTripletNetwork, "MultiTripletNetwork"),
|
|
192
|
+
"CrossModalPred": (CrossModalPred, "CrossModalPred"),
|
|
193
|
+
"GNN": (GNN, "GNN"),
|
|
194
|
+
"RandomForest": ("RandomForest", None),
|
|
195
|
+
"SVM": ("SVM", None),
|
|
196
|
+
"RandomSurvivalForest": ("RandomSurvivalForest", None)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
model_info = available_models.get(args.model_class)
|
|
200
|
+
|
|
201
|
+
if model_info is None:
|
|
202
|
+
raise ValueError(f"Unsupported model class {args.model_class}")
|
|
203
|
+
|
|
204
|
+
# Unpack the tuple into model class and config name
|
|
205
|
+
model_class, config_name = model_info
|
|
203
206
|
|
|
204
207
|
# import assays and labels
|
|
205
208
|
inputDir = args.data_path
|
|
@@ -221,6 +224,34 @@ def main():
|
|
|
221
224
|
downsample = args.subsample)
|
|
222
225
|
train_dataset, test_dataset = data_importer.import_data()
|
|
223
226
|
|
|
227
|
+
if args.model_class in ["RandomForest", "SVM"]:
|
|
228
|
+
if args.target_variables:
|
|
229
|
+
var = args.target_variables.strip().split(',')[0]
|
|
230
|
+
print(f"Training {args.model_class} on variable: {var}")
|
|
231
|
+
metrics = flexynesis.evaluate_baseline_performance(train_dataset, test_dataset, variable_name=var,
|
|
232
|
+
methods=[args.model_class], n_folds=5, n_jobs=args.threads)
|
|
233
|
+
metrics.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'stats.csv'])), header=True, index=False)
|
|
234
|
+
print(f"{args.model_class} evaluation complete. Results saved.")
|
|
235
|
+
# we skip everything related to deep learning models here
|
|
236
|
+
sys.exit(0)
|
|
237
|
+
else:
|
|
238
|
+
raise ValueError(f"At least one target variable is required to run RandomForest/SVM models. Set --target_variables argument")
|
|
239
|
+
|
|
240
|
+
if args.model_class == "RandomSurvivalForest":
|
|
241
|
+
if args.surv_event_var and args.surv_time_var:
|
|
242
|
+
print(f"Training {args.model_class} on survival variables: {args.surv_event_var} and {args.surv_time_var}")
|
|
243
|
+
metrics = flexynesis.evaluate_baseline_survival_performance(train_dataset, test_dataset,
|
|
244
|
+
args.surv_time_var,
|
|
245
|
+
args.surv_event_var,
|
|
246
|
+
n_folds = 5,
|
|
247
|
+
n_jobs = int(args.threads))
|
|
248
|
+
metrics.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'stats.csv'])), header=True, index=False)
|
|
249
|
+
print(f"{args.model_class} evaluation complete. Results saved.")
|
|
250
|
+
# we skip everything related to deep learning models here
|
|
251
|
+
sys.exit(0)
|
|
252
|
+
else:
|
|
253
|
+
raise ValueError(f"Missing survival variables. Set --surv_event_var --surv_time_var arguments")
|
|
254
|
+
|
|
224
255
|
if args.model_class == 'GNN':
|
|
225
256
|
# overlay datasets with network info
|
|
226
257
|
# this is a temporary solution
|
|
@@ -282,20 +313,16 @@ def main():
|
|
|
282
313
|
# update the test dataset to exclude finetuning samples
|
|
283
314
|
test_dataset = holdout_dataset
|
|
284
315
|
|
|
316
|
+
# get sample embeddings and save
|
|
317
|
+
print("[INFO] Extracting sample embeddings")
|
|
318
|
+
embeddings_train = model.transform(train_dataset)
|
|
319
|
+
embeddings_test = model.transform(test_dataset)
|
|
320
|
+
|
|
321
|
+
embeddings_train.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'embeddings_train.csv'])), header=True)
|
|
322
|
+
embeddings_test.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'embeddings_test.csv'])), header=True)
|
|
323
|
+
|
|
285
324
|
# evaluate predictions; (if any supervised learning happened)
|
|
286
325
|
if any([args.target_variables, args.surv_event_var, args.batch_variables]):
|
|
287
|
-
print("[INFO] Computing model evaluation metrics")
|
|
288
|
-
metrics_df = flexynesis.evaluate_wrapper(model.predict(test_dataset), test_dataset,
|
|
289
|
-
surv_event_var=model.surv_event_var,
|
|
290
|
-
surv_time_var=model.surv_time_var)
|
|
291
|
-
metrics_df.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'stats.csv'])), header=True, index=False)
|
|
292
|
-
|
|
293
|
-
# print known/predicted labels
|
|
294
|
-
predicted_labels = pd.concat([flexynesis.get_predicted_labels(model.predict(train_dataset), train_dataset, 'train'),
|
|
295
|
-
flexynesis.get_predicted_labels(model.predict(test_dataset), test_dataset, 'test')],
|
|
296
|
-
ignore_index=True)
|
|
297
|
-
predicted_labels.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'predicted_labels.csv'])), header=True, index=False)
|
|
298
|
-
|
|
299
326
|
if not args.disable_marker_finding: # unless marker discovery is disabled
|
|
300
327
|
# compute feature importance values
|
|
301
328
|
print("[INFO] Computing variable importance scores")
|
|
@@ -305,14 +332,19 @@ def main():
|
|
|
305
332
|
ignore_index = True)
|
|
306
333
|
df_imp.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'feature_importance.csv'])), header=True, index=False)
|
|
307
334
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
335
|
+
# print known/predicted labels
|
|
336
|
+
predicted_labels = pd.concat([flexynesis.get_predicted_labels(model.predict(train_dataset), train_dataset, 'train'),
|
|
337
|
+
flexynesis.get_predicted_labels(model.predict(test_dataset), test_dataset, 'test')],
|
|
338
|
+
ignore_index=True)
|
|
339
|
+
predicted_labels.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'predicted_labels.csv'])), header=True, index=False)
|
|
340
|
+
|
|
341
|
+
print("[INFO] Computing model evaluation metrics")
|
|
342
|
+
metrics_df = flexynesis.evaluate_wrapper(args.model_class, model.predict(test_dataset), test_dataset,
|
|
343
|
+
surv_event_var=model.surv_event_var,
|
|
344
|
+
surv_time_var=model.surv_time_var)
|
|
345
|
+
metrics_df.to_csv(os.path.join(args.outdir, '.'.join([args.prefix, 'stats.csv'])), header=True, index=False)
|
|
346
|
+
|
|
347
|
+
|
|
316
348
|
# also filter embeddings to remove batch-associated dims and only keep target-variable associated dims
|
|
317
349
|
if args.batch_variables is not None:
|
|
318
350
|
print("[INFO] Printing filtered embeddings")
|
|
@@ -339,7 +371,7 @@ def main():
|
|
|
339
371
|
|
|
340
372
|
|
|
341
373
|
# evaluate off-the-shelf methods on the main target variable
|
|
342
|
-
if args.evaluate_baseline_performance
|
|
374
|
+
if args.evaluate_baseline_performance:
|
|
343
375
|
print("[INFO] Computing off-the-shelf method performance on first target variable:",model.target_variables[0])
|
|
344
376
|
var = model.target_variables[0]
|
|
345
377
|
metrics = pd.DataFrame()
|
|
@@ -351,9 +383,10 @@ def main():
|
|
|
351
383
|
|
|
352
384
|
if var != model.surv_event_var:
|
|
353
385
|
metrics = flexynesis.evaluate_baseline_performance(train, test,
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
386
|
+
variable_name = var,
|
|
387
|
+
methods = ['RandomForest', 'SVM'],
|
|
388
|
+
n_folds = 5,
|
|
389
|
+
n_jobs = int(args.threads))
|
|
357
390
|
if model.surv_event_var and model.surv_time_var:
|
|
358
391
|
print("[INFO] Computing off-the-shelf method performance on survival variable:",model.surv_time_var)
|
|
359
392
|
metrics_baseline_survival = flexynesis.evaluate_baseline_survival_performance(train, test,
|
|
@@ -369,5 +402,7 @@ def main():
|
|
|
369
402
|
# save the trained model in file
|
|
370
403
|
torch.save(model, os.path.join(args.outdir, '.'.join([args.prefix, 'final_model.pth'])))
|
|
371
404
|
|
|
405
|
+
|
|
372
406
|
if __name__ == "__main__":
|
|
373
407
|
main()
|
|
408
|
+
print("[INFO] Finished the analysis!")
|
|
@@ -627,13 +627,14 @@ class TripletMultiOmicDataset(Dataset):
|
|
|
627
627
|
for label in labels_set}
|
|
628
628
|
return labels_set, label_to_indices
|
|
629
629
|
|
|
630
|
+
|
|
630
631
|
class MultiOmicDatasetNW(Dataset):
|
|
631
632
|
def __init__(self, multiomic_dataset, interaction_df):
|
|
632
633
|
self.multiomic_dataset = multiomic_dataset
|
|
633
634
|
self.interaction_df = interaction_df
|
|
634
635
|
|
|
635
|
-
#
|
|
636
|
-
self.common_features = self.
|
|
636
|
+
# Compute union of features in the data matrices that also appear in the network
|
|
637
|
+
self.common_features = self.find_union_features()
|
|
637
638
|
self.gene_to_index = {gene: idx for idx, gene in enumerate(self.common_features)}
|
|
638
639
|
self.edge_index = self.create_edge_index()
|
|
639
640
|
self.samples = self.multiomic_dataset.samples
|
|
@@ -647,38 +648,48 @@ class MultiOmicDatasetNW(Dataset):
|
|
|
647
648
|
# Store labels for all samples
|
|
648
649
|
self.labels = {target_name: labels for target_name, labels in self.multiomic_dataset.ann.items()}
|
|
649
650
|
|
|
650
|
-
def
|
|
651
|
-
|
|
651
|
+
def find_union_features(self):
|
|
652
|
+
# Find the union of all features in the multiomic dataset
|
|
653
|
+
all_omic_features = set().union(*(set(features) for features in self.multiomic_dataset.features.values()))
|
|
654
|
+
# Find the union of proteins involved in interactions
|
|
652
655
|
interaction_genes = set(self.interaction_df['protein1']).union(set(self.interaction_df['protein2']))
|
|
653
|
-
|
|
656
|
+
# Return the intersection of omic features and interaction genes
|
|
657
|
+
return list(all_omic_features.intersection(interaction_genes))
|
|
654
658
|
|
|
655
659
|
def create_edge_index(self):
|
|
660
|
+
# Create edges only if both proteins are within the available features
|
|
656
661
|
filtered_df = self.interaction_df[
|
|
657
662
|
(self.interaction_df['protein1'].isin(self.common_features)) &
|
|
658
663
|
(self.interaction_df['protein2'].isin(self.common_features))
|
|
659
664
|
]
|
|
660
665
|
edge_list = [(self.gene_to_index[row['protein1']], self.gene_to_index[row['protein2']]) for index, row in filtered_df.iterrows()]
|
|
661
666
|
return torch.tensor(edge_list, dtype=torch.long).t()
|
|
662
|
-
|
|
667
|
+
|
|
663
668
|
def precompute_node_features(self):
|
|
664
|
-
# Find indices of common features in each data matrix
|
|
665
|
-
feature_indices = {data_type: [self.multiomic_dataset.features[data_type].get_loc(gene)
|
|
666
|
-
for gene in self.common_features]
|
|
667
|
-
for data_type in self.multiomic_dataset.dat}
|
|
668
|
-
# Create a tensor to store all features [num_samples, num_nodes, num_data_types]
|
|
669
669
|
num_samples = len(self.samples)
|
|
670
670
|
num_nodes = len(self.common_features)
|
|
671
671
|
num_data_types = len(self.multiomic_dataset.dat)
|
|
672
|
-
all_features = torch.
|
|
672
|
+
all_features = torch.full((num_samples, num_nodes, num_data_types), float('nan'), dtype=torch.float)
|
|
673
673
|
|
|
674
|
-
# Extract features for each data type and place them in the tensor
|
|
675
674
|
for i, data_type in enumerate(self.multiomic_dataset.dat):
|
|
676
|
-
# Get the data matrix
|
|
677
675
|
data_matrix = self.multiomic_dataset.dat[data_type]
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
676
|
+
feature_indices = {
|
|
677
|
+
gene: self.multiomic_dataset.features[data_type].get_loc(gene)
|
|
678
|
+
for gene in self.common_features if gene in self.multiomic_dataset.features[data_type]
|
|
679
|
+
}
|
|
680
|
+
valid_indices = torch.tensor(list(feature_indices.values()))
|
|
681
|
+
feature_positions = torch.tensor([self.gene_to_index[gene] for gene in feature_indices.keys()])
|
|
682
|
+
|
|
683
|
+
# Fill in the available data
|
|
684
|
+
all_features[:, feature_positions, i] = data_matrix[:, valid_indices]
|
|
685
|
+
|
|
686
|
+
# Precompute medians for all data types, ignoring NaN values
|
|
687
|
+
medians = torch.nanmedian(all_features, dim=1, keepdim=True).values # Use .values to get the actual median tensor
|
|
688
|
+
|
|
689
|
+
# Replace all NaN values in all_features with their corresponding median values
|
|
690
|
+
isnan = torch.isnan(all_features)
|
|
691
|
+
all_features[isnan] = medians.expand_as(all_features)[isnan]
|
|
692
|
+
|
|
682
693
|
return all_features
|
|
683
694
|
|
|
684
695
|
def subset(self, indices):
|
|
@@ -199,6 +199,23 @@ def evaluate_survival(outputs, durations, events):
|
|
|
199
199
|
return {'cindex': c_index}
|
|
200
200
|
|
|
201
201
|
def evaluate_classifier(y_true, y_pred, print_report = False):
|
|
202
|
+
"""
|
|
203
|
+
Evaluate the performance of a classifier using multiple metrics and optionally print a detailed classification report.
|
|
204
|
+
|
|
205
|
+
This function computes balanced accuracy, F1 score (macro), and Cohen's Kappa score for the given true and predicted labels.
|
|
206
|
+
If `print_report` is set to True, it prints a detailed classification report.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
y_true (array-like): True labels of the data, must be 1D list or array of labels.
|
|
210
|
+
y_pred (array-like): Predicted labels as returned by a classifier, must match the dimensions of y_true.
|
|
211
|
+
print_report (bool, optional): If True, prints a detailed classification report. Defaults to False.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
dict: A dictionary containing:
|
|
215
|
+
- 'balanced_acc': The balanced accuracy of the predictions.
|
|
216
|
+
- 'f1_score': The macro-average F1 score of the predictions.
|
|
217
|
+
- 'kappa': Cohen's Kappa score indicating the level of agreement between the true and predicted labels.
|
|
218
|
+
"""
|
|
202
219
|
# Balanced accuracy
|
|
203
220
|
balanced_acc = balanced_accuracy_score(y_true, y_pred)
|
|
204
221
|
# F1 score (macro)
|
|
@@ -213,12 +230,48 @@ def evaluate_classifier(y_true, y_pred, print_report = False):
|
|
|
213
230
|
return {"balanced_acc": balanced_acc, "f1_score": f1, "kappa": kappa}
|
|
214
231
|
|
|
215
232
|
def evaluate_regressor(y_true, y_pred):
|
|
233
|
+
"""
|
|
234
|
+
Evaluate the performance of a regression model using mean squared error, R-squared, and Pearson correlation coefficient.
|
|
235
|
+
|
|
236
|
+
This function computes the mean squared error (MSE) between true and predicted values as a measure of prediction accuracy.
|
|
237
|
+
It also performs a linear regression analysis between the true and predicted values to obtain the R-squared value, which
|
|
238
|
+
explains the variance ratio, and the Pearson correlation coefficient, providing insight into the linear relationship strength.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
y_true (array-like): True values of the dependent variable, must be a 1D list or array.
|
|
242
|
+
y_pred (array-like): Predicted values as returned by a regressor, must match the dimensions of y_true.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
dict: A dictionary containing:
|
|
246
|
+
- 'mse': The mean squared error between the true and predicted values.
|
|
247
|
+
- 'r2': The R-squared value indicating the proportion of variance in the dependent variable predictable from the independent variable.
|
|
248
|
+
- 'pearson_corr': The Pearson correlation coefficient indicating the linear relationship strength between the true and predicted values.
|
|
249
|
+
"""
|
|
216
250
|
mse = mean_squared_error(y_true, y_pred)
|
|
217
251
|
slope, intercept, r_value, p_value, std_err = linregress(y_true,y_pred)
|
|
218
252
|
r2 = r_value**2
|
|
219
253
|
return {"mse": mse, "r2": r2, "pearson_corr": r_value}
|
|
220
254
|
|
|
221
|
-
def evaluate_wrapper(y_pred_dict, dataset, surv_event_var = None, surv_time_var = None):
|
|
255
|
+
def evaluate_wrapper(method, y_pred_dict, dataset, surv_event_var = None, surv_time_var = None):
|
|
256
|
+
"""
|
|
257
|
+
Evaluates predictions for different variables within a dataset using appropriate metrics based on the variable type.
|
|
258
|
+
Supports evaluation for numerical, categorical, and survival data.
|
|
259
|
+
|
|
260
|
+
This function loops through each variable in the predictions dictionary, determines the type of the variable,
|
|
261
|
+
and evaluates the predictions using the appropriate method: regression, classification, or survival analysis.
|
|
262
|
+
It compiles the metrics into a list of dictionaries, which is then converted into a pandas DataFrame.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
method (str): Identifier for the prediction method or model used.
|
|
266
|
+
y_pred_dict (dict): A dictionary where keys are variable names and values are arrays of predicted values.
|
|
267
|
+
dataset (Dataset): A dataset object containing actual values and metadata such as variable types.
|
|
268
|
+
surv_event_var (str, optional): The name of the survival event variable. Required if survival analysis is performed.
|
|
269
|
+
surv_time_var (str, optional): The name of the survival time variable. Required if survival analysis is performed.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
pd.DataFrame: A DataFrame where each row contains the method, variable name, variable type, metric name, and metric value.
|
|
273
|
+
|
|
274
|
+
"""
|
|
222
275
|
metrics_list = []
|
|
223
276
|
for var in y_pred_dict.keys():
|
|
224
277
|
if dataset.variable_types[var] == 'numerical':
|
|
@@ -235,6 +288,7 @@ def evaluate_wrapper(y_pred_dict, dataset, surv_event_var = None, surv_time_var
|
|
|
235
288
|
|
|
236
289
|
for metric, value in metrics.items():
|
|
237
290
|
metrics_list.append({
|
|
291
|
+
'method': method,
|
|
238
292
|
'var': var,
|
|
239
293
|
'variable_type': dataset.variable_types[var],
|
|
240
294
|
'metric': metric,
|
|
@@ -263,8 +317,26 @@ def get_predicted_labels(y_pred_dict, dataset, split):
|
|
|
263
317
|
dfs.append(df)
|
|
264
318
|
return pd.concat(dfs, ignore_index=True)
|
|
265
319
|
|
|
266
|
-
|
|
267
|
-
|
|
320
|
+
def evaluate_baseline_performance(train_dataset, test_dataset, variable_name, methods, n_folds=5, n_jobs=4):
|
|
321
|
+
"""
|
|
322
|
+
Evaluates the performance of RandomForest and/or Support Vector Machine models on a given variable from the provided datasets using cross-validation.
|
|
323
|
+
|
|
324
|
+
This function preprocesses the training and testing data, performs grid search with cross-validation to find the best
|
|
325
|
+
hyperparameters for the specified methods, and then evaluates the performance of these models on the testing set.
|
|
326
|
+
It supports evaluation for both categorical and numerical variables using appropriate machine learning models.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
train_dataset (Dataset): A MultiOmicDataset object containing training data and metadata such as variable types.
|
|
330
|
+
test_dataset (Dataset): A MultiOmicDataset object containing testing data.
|
|
331
|
+
variable_name (str): The name of the target variable for prediction.
|
|
332
|
+
methods (list of str): List of machine learning methods to evaluate, e.g., ['RandomForest', 'SVM'].
|
|
333
|
+
n_folds (int, optional): Number of folds to use in K-fold cross-validation. Defaults to 5.
|
|
334
|
+
n_jobs (int, optional): Number of jobs to run in parallel during grid search. Defaults to 4.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
pd.DataFrame: A DataFrame containing the method, variable name, variable type, metric name, and metric value for each tested method.
|
|
338
|
+
|
|
339
|
+
"""
|
|
268
340
|
def prepare_data(data_object):
|
|
269
341
|
# Concatenate Data Matrices
|
|
270
342
|
X = np.concatenate([tensor for tensor in data_object.dat.values()], axis=1)
|
|
@@ -281,52 +353,32 @@ def evaluate_baseline_performance(train_dataset, test_dataset, variable_name, n_
|
|
|
281
353
|
# Determine variable type
|
|
282
354
|
variable_type = train_dataset.variable_types[variable_name]
|
|
283
355
|
|
|
284
|
-
# Initialize models and parameter grids
|
|
285
|
-
if variable_type == 'categorical':
|
|
286
|
-
model_params = {
|
|
287
|
-
'RandomForestClassifier': {
|
|
288
|
-
'model': RandomForestClassifier(random_state=42),
|
|
289
|
-
'params': {
|
|
290
|
-
'n_estimators': [100, 200, 300],
|
|
291
|
-
'max_depth': [10, 20, None]
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
'SVC': {
|
|
295
|
-
'model': SVC(),
|
|
296
|
-
'params': {
|
|
297
|
-
'C': [0.1, 1, 10],
|
|
298
|
-
'kernel': ['rbf', 'poly']
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
elif variable_type == 'numerical':
|
|
303
|
-
model_params = {
|
|
304
|
-
'RandomForestRegressor': {
|
|
305
|
-
'model': RandomForestRegressor(random_state=42),
|
|
306
|
-
'params': {
|
|
307
|
-
'n_estimators': [100, 200, 300],
|
|
308
|
-
'max_depth': [10, 20, None]
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
'SVR': {
|
|
312
|
-
'model': SVR(),
|
|
313
|
-
'params': {
|
|
314
|
-
'C': [0.1, 1, 10],
|
|
315
|
-
'kernel': ['rbf', 'poly']
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
356
|
# Cross-Validation and Training
|
|
321
357
|
kf = KFold(n_splits=n_folds, shuffle=True, random_state=42)
|
|
322
358
|
X_train, y_train = prepare_data(train_dataset)
|
|
323
|
-
print("Train:",X_train.shape)
|
|
359
|
+
print("Train:", X_train.shape)
|
|
324
360
|
X_test, y_test = prepare_data(test_dataset)
|
|
325
|
-
print("Test:",X_test.shape)
|
|
361
|
+
print("Test:", X_test.shape)
|
|
326
362
|
|
|
327
363
|
metrics_list = []
|
|
328
|
-
|
|
329
|
-
|
|
364
|
+
|
|
365
|
+
for method in methods:
|
|
366
|
+
if variable_type == 'categorical':
|
|
367
|
+
if method == 'RandomForest':
|
|
368
|
+
model = RandomForestClassifier(random_state=42)
|
|
369
|
+
params = {'n_estimators': [100, 200, 300], 'max_depth': [10, 20, None]}
|
|
370
|
+
elif method == 'SVM':
|
|
371
|
+
model = SVC(random_state=42)
|
|
372
|
+
params = {'C': [0.1, 1, 10], 'kernel': ['rbf', 'poly']}
|
|
373
|
+
elif variable_type == 'numerical':
|
|
374
|
+
if method == 'RandomForest':
|
|
375
|
+
model = RandomForestRegressor(random_state=42)
|
|
376
|
+
params = {'n_estimators': [100, 200, 300], 'max_depth': [10, 20, None]}
|
|
377
|
+
elif method == 'SVM':
|
|
378
|
+
model = SVR()
|
|
379
|
+
params = {'C': [0.1, 1, 10], 'kernel': ['rbf', 'poly']}
|
|
380
|
+
|
|
381
|
+
grid_search = GridSearchCV(model, params, cv=kf, n_jobs=n_jobs)
|
|
330
382
|
grid_search.fit(X_train, y_train)
|
|
331
383
|
best_model = grid_search.best_estimator_
|
|
332
384
|
|
|
@@ -341,7 +393,7 @@ def evaluate_baseline_performance(train_dataset, test_dataset, variable_name, n_
|
|
|
341
393
|
|
|
342
394
|
for metric, value in metrics.items():
|
|
343
395
|
metrics_list.append({
|
|
344
|
-
'method':
|
|
396
|
+
'method': method + ('Classifier' if variable_type == 'categorical' else 'Regressor'),
|
|
345
397
|
'var': variable_name,
|
|
346
398
|
'variable_type': variable_type,
|
|
347
399
|
'metric': metric,
|
|
@@ -352,6 +404,26 @@ def evaluate_baseline_performance(train_dataset, test_dataset, variable_name, n_
|
|
|
352
404
|
return pd.DataFrame(metrics_list)
|
|
353
405
|
|
|
354
406
|
def evaluate_baseline_survival_performance(train_dataset, test_dataset, duration_col, event_col, n_folds=5, n_jobs=4):
|
|
407
|
+
"""
|
|
408
|
+
Evaluates the baseline performance of a Random Survival Forest model on survival data using the Concordance Index.
|
|
409
|
+
|
|
410
|
+
The function preprocesses both training and testing datasets to prepare appropriate survival data (comprising durations
|
|
411
|
+
and event occurrences), performs cross-validation to assess model robustness, and then calculates the Concordance Index on
|
|
412
|
+
the test data. It uses a Random Survival Forest (RSF) as the predictive model.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
train_dataset (Dataset): The training dataset (a MultiOmicDataset object) containing features and survival data.
|
|
416
|
+
test_dataset (Dataset): The testing dataset (a MultiOmicDataset object) containing features and survival data.
|
|
417
|
+
duration_col (str): Column name in the dataset for survival time.
|
|
418
|
+
event_col (str): Column name in the dataset for the event occurrence (1 if event occurred, 0 otherwise).
|
|
419
|
+
n_folds (int, optional): Number of folds for K-fold cross-validation. Defaults to 5.
|
|
420
|
+
n_jobs (int, optional): Number of parallel jobs to run for Random Survival Forest training. Defaults to 4.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
pd.DataFrame: A DataFrame containing the performance metrics of the RSF model, specifically the Concordance Index,
|
|
424
|
+
listed along with the method name and variable details.
|
|
425
|
+
|
|
426
|
+
"""
|
|
355
427
|
print(f"[INFO] Evaluating baseline survival prediction performance")
|
|
356
428
|
def prepare_data(data_object, duration_col, event_col):
|
|
357
429
|
# Concatenate Data Matrices
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flexynesis
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: A deep-learning based multi-omics bulk sequencing data integration suite with a focus on (pre-)clinical endpoint prediction.
|
|
5
5
|
Author-email: Bora Uyar <bora.uyar@mdc-berlin.de>, Taras Savchyn <Taras.Savchyn@mdc-berlin.de>, Ricardo Wurmus <Ricardo.Wurmus@mdc-berlin.de>, Ahmet Sarigun <Ahmet.Sariguen@mdc-berlin.de>
|
|
6
6
|
Project-URL: homepage, https://github.com/BIMSBbioinfo/flexynesis
|
|
@@ -113,7 +113,7 @@ flexynesis --data_path dataset1 \
|
|
|
113
113
|
--prefix erlotinib_direct \
|
|
114
114
|
--early_stop_patience 3 \
|
|
115
115
|
--use_loss_weighting False \
|
|
116
|
-
--evaluate_baseline_performance
|
|
116
|
+
--evaluate_baseline_performance
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
## Accelerating with GPUs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|