dietnetwork 0.3.0__py3-none-any.whl
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.
- dietnet/__init__.py +11 -0
- dietnet/_launch.py +40 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/__init__.py +0 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/compute_input_features_statistics.py +221 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/generate_embedding.py +216 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/__init__.py +0 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/dataset_utils.py +200 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/log_utils.py +148 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/mainloop_utils.py +395 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/model.py +489 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/model_handlers.py +89 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/helpers/task_handlers.py +361 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/partition_data.py +331 -0
- dietnet/_upstream/Dietnet_train_and_evaluate/train.py +959 -0
- dietnet/align.py +188 -0
- dietnet/alphagenome.py +208 -0
- dietnet/cli.py +479 -0
- dietnet/dataset.py +181 -0
- dietnet/features.py +267 -0
- dietnet/package.py +187 -0
- dietnet/plink.py +163 -0
- dietnet/predict.py +123 -0
- dietnet/published.py +87 -0
- dietnet/py.typed +0 -0
- dietnet/training.py +176 -0
- dietnet/upstream.py +161 -0
- dietnet/upstream_manifest.json +53 -0
- dietnetwork-0.3.0.dist-info/METADATA +249 -0
- dietnetwork-0.3.0.dist-info/RECORD +33 -0
- dietnetwork-0.3.0.dist-info/WHEEL +4 -0
- dietnetwork-0.3.0.dist-info/entry_points.txt +2 -0
- dietnetwork-0.3.0.dist-info/licenses/LICENSE +28 -0
- dietnetwork-0.3.0.dist-info/licenses/NOTICE +15 -0
dietnet/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Diet Network for genetic ancestry prediction.
|
|
2
|
+
|
|
3
|
+
The model, training loop and the statistics it is trained on are HussinLab's
|
|
4
|
+
published code (Rochefort-Boulanger, Scicluna et al., AJHG 2026), shipped
|
|
5
|
+
unmodified and called from here; see :mod:`dietnet.upstream`. This package adds
|
|
6
|
+
what the published scripts leave to the user: building their HDF5 input from
|
|
7
|
+
PLINK, allele-aware matching of new cohorts, portable model packages, ensembles,
|
|
8
|
+
biobank-scale inference and a command line around all of it.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "0.3.0"
|
dietnet/_launch.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Run one of upstream's scripts, unmodified, under current torch.
|
|
2
|
+
|
|
3
|
+
Usage (by :func:`dietnet.upstream.run`): ``python -m dietnet._launch SCRIPT [ARGS...]``.
|
|
4
|
+
|
|
5
|
+
upstream's train.py saves its validation results (numpy float64 scalars) in its
|
|
6
|
+
checkpoint, then reloads it with a bare ``torch.load``. Since torch 2.6 that
|
|
7
|
+
call defaults to ``weights_only=True`` and refuses numpy scalars. Rather than
|
|
8
|
+
switching the safe loader off, this allowlists exactly the numpy types such a
|
|
9
|
+
checkpoint holds -- the remedy torch's own error message gives -- and then runs
|
|
10
|
+
the script as ``python SCRIPT`` would, from its own directory.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import runpy
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
import torch
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _allow_numpy_scalars() -> None:
|
|
22
|
+
try:
|
|
23
|
+
from numpy._core.multiarray import scalar # numpy >= 2
|
|
24
|
+
except ImportError: # pragma: no cover - numpy 1.x
|
|
25
|
+
from numpy.core.multiarray import scalar
|
|
26
|
+
torch.serialization.add_safe_globals(
|
|
27
|
+
[scalar, np.dtype, type(np.dtype(np.float64)), type(np.dtype(np.int64))]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main() -> None:
|
|
32
|
+
script = Path(sys.argv[1]).resolve()
|
|
33
|
+
_allow_numpy_scalars()
|
|
34
|
+
sys.argv = [str(script), *sys.argv[2:]]
|
|
35
|
+
sys.path.insert(0, str(script.parent))
|
|
36
|
+
runpy.run_path(str(script), run_name="__main__")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import multiprocessing as mp
|
|
4
|
+
import time
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
import h5py
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
start_time = time.time()
|
|
14
|
+
args = parse_args()
|
|
15
|
+
|
|
16
|
+
# Flag to compute only mean (True or False)
|
|
17
|
+
mean_only = args.mean_only
|
|
18
|
+
|
|
19
|
+
# Load indices of every fold
|
|
20
|
+
partition_data = np.load(args.partition, allow_pickle=True)
|
|
21
|
+
indices_byfold = partition_data['folds_indexes']
|
|
22
|
+
|
|
23
|
+
# Dataset
|
|
24
|
+
dataset_file = h5py.File(args.dataset, 'r')
|
|
25
|
+
|
|
26
|
+
# Compute inp feat stats by fold
|
|
27
|
+
means_by_fold = []
|
|
28
|
+
sds_by_fold = []
|
|
29
|
+
for fold in range(len(indices_byfold)):
|
|
30
|
+
print('\n---\nComputing input features statistic of fold', str(fold))
|
|
31
|
+
fold_start_time = time.time()
|
|
32
|
+
|
|
33
|
+
# Indices of the fold
|
|
34
|
+
fold_indices = indices_byfold[fold]
|
|
35
|
+
|
|
36
|
+
# Genotypes of training samples
|
|
37
|
+
train_indices = np.sort(fold_indices[0]) # sort is a hdf5 requirement
|
|
38
|
+
x_train = dataset_file['inputs'][train_indices]
|
|
39
|
+
|
|
40
|
+
# Compute statistics of every features in parallel
|
|
41
|
+
ncpus = args.ncpus
|
|
42
|
+
print('Computing input features statistics of {} SNPs for {} '
|
|
43
|
+
'samples using {} cpu(s)'.format(
|
|
44
|
+
x_train.shape[1], x_train.shape[0], ncpus))
|
|
45
|
+
|
|
46
|
+
pool = mp.Pool(processes=ncpus) #this starts ncpus nb of processes
|
|
47
|
+
|
|
48
|
+
# Results that will be returned by processes
|
|
49
|
+
results = []
|
|
50
|
+
|
|
51
|
+
# The statistics of every snp feature is computed by a different process
|
|
52
|
+
nb_snps = x_train.shape[1]
|
|
53
|
+
for i in range(nb_snps):
|
|
54
|
+
# Compute snps mean
|
|
55
|
+
if mean_only :
|
|
56
|
+
results.append(
|
|
57
|
+
pool.apply_async(
|
|
58
|
+
compute_feature_mean, args=(i, x_train[:,i])))
|
|
59
|
+
|
|
60
|
+
# Compute mean and standard deviation (sd)
|
|
61
|
+
else:
|
|
62
|
+
results.append(
|
|
63
|
+
pool.apply_async(
|
|
64
|
+
compute_feature_mean_and_sd, args=(i, x_train[:,i])))
|
|
65
|
+
|
|
66
|
+
# Get order (snp nb) and mean of features from multiprocess results
|
|
67
|
+
print('Computed statistics of all input features, getting the results')
|
|
68
|
+
results_values = np.array([np.array(p.get()) for p in results])
|
|
69
|
+
|
|
70
|
+
# Order stats based on snp nb
|
|
71
|
+
print('Ordering {} means returned by multiprocessing'.format(
|
|
72
|
+
len(results_values[:,1])))
|
|
73
|
+
idx = results_values[:,0].astype(int)
|
|
74
|
+
ordered_idx_pos = idx.argsort()
|
|
75
|
+
|
|
76
|
+
# Ordered means
|
|
77
|
+
means = results_values[:,1][ordered_idx_pos]
|
|
78
|
+
|
|
79
|
+
# Ordered sd
|
|
80
|
+
if not mean_only:
|
|
81
|
+
print('Ordering {} standard deviations returned by multiprocessing'.format(
|
|
82
|
+
len(results_values[:,2])))
|
|
83
|
+
sds = results_values[:,2][ordered_idx_pos]
|
|
84
|
+
|
|
85
|
+
# Append fold statistics
|
|
86
|
+
means_by_fold.append(means)
|
|
87
|
+
sds_by_fold.append(sds)
|
|
88
|
+
|
|
89
|
+
print('Computed fold input features stats in {} seconds\n---\n'.format(
|
|
90
|
+
time.time()-fold_start_time))
|
|
91
|
+
|
|
92
|
+
# Close access to h5py dataset file
|
|
93
|
+
dataset_file.close()
|
|
94
|
+
|
|
95
|
+
# Write input features stats to file
|
|
96
|
+
if args.out is not None:
|
|
97
|
+
stats_filename = args.out
|
|
98
|
+
else:
|
|
99
|
+
stats_filename = 'input_features_stats_' \
|
|
100
|
+
+ args.dataset.split('/')[-1][0:-5] \
|
|
101
|
+
+ '_' \
|
|
102
|
+
+ datetime.now().strftime("%Y_%m_%d_%Hh%Mmin%Ssec")
|
|
103
|
+
|
|
104
|
+
stats_fullpath = os.path.join(args.exp_path, stats_filename)
|
|
105
|
+
|
|
106
|
+
if mean_only:
|
|
107
|
+
np.savez(stats_fullpath,
|
|
108
|
+
means_by_fold=means_by_fold)
|
|
109
|
+
|
|
110
|
+
else:
|
|
111
|
+
np.savez(stats_fullpath,
|
|
112
|
+
means_by_fold=means_by_fold,
|
|
113
|
+
sd_by_fold=sds_by_fold)
|
|
114
|
+
|
|
115
|
+
print('\n---')
|
|
116
|
+
print('Input features stats were saved to {}'.format(stats_fullpath))
|
|
117
|
+
print('Execution time {} seconds\n---\n'.format(
|
|
118
|
+
time.time() - start_time))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def compute_feature_mean(i, x):
|
|
122
|
+
"""
|
|
123
|
+
i is the snp nb (from 0 to nb_snps-1)
|
|
124
|
+
x is the vector of genotypes for the snp (dim : nb_samples x 1)
|
|
125
|
+
"""
|
|
126
|
+
# Set to false missing value (missing values are encoded with -1)
|
|
127
|
+
mask = (x >= 0)
|
|
128
|
+
|
|
129
|
+
# Compute mean :
|
|
130
|
+
# in numerator, missing values are 0
|
|
131
|
+
# in the denominator we divise by the nb of non missing values
|
|
132
|
+
mean = np.sum(x*mask) / np.sum(mask)
|
|
133
|
+
|
|
134
|
+
return (i,mean)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def compute_feature_mean_and_sd(i, x):
|
|
138
|
+
"""
|
|
139
|
+
i is the snp nb (from 0 to nb_snps-1)
|
|
140
|
+
x is the vector of genotypes for the snp (dim : nb_samples x 1)
|
|
141
|
+
"""
|
|
142
|
+
# Compute mean
|
|
143
|
+
_,mean = compute_feature_mean(i,x)
|
|
144
|
+
|
|
145
|
+
# --- Compute sd ---
|
|
146
|
+
mask = (x >= 0) #set missing values (-1) to False
|
|
147
|
+
|
|
148
|
+
numerator = np.sum((x*mask - mean*mask)**2)
|
|
149
|
+
|
|
150
|
+
denominator = np.sum(mask) - 1
|
|
151
|
+
|
|
152
|
+
sd = np.sqrt(numerator/denominator)
|
|
153
|
+
|
|
154
|
+
sd += 1e-6
|
|
155
|
+
|
|
156
|
+
return (i,mean,sd)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def parse_args():
|
|
160
|
+
parser = argparse.ArgumentParser(
|
|
161
|
+
description=('Compute features means and standard deviations '
|
|
162
|
+
'for missing values filing and input normalization '
|
|
163
|
+
'at training time')
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Path
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
'--exp-path',
|
|
169
|
+
type=str,
|
|
170
|
+
required=True,
|
|
171
|
+
help=('Path to directory where input features statistics will '
|
|
172
|
+
'be written')
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Files
|
|
176
|
+
parser.add_argument(
|
|
177
|
+
'--dataset',
|
|
178
|
+
type=str,
|
|
179
|
+
required=True,
|
|
180
|
+
help=('Hdf5 dataset created with create_dataset.py. '
|
|
181
|
+
'Provide full path')
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
'--partition',
|
|
186
|
+
type=str,
|
|
187
|
+
required=True,
|
|
188
|
+
help=('Npz dataset partition returned by partition_data.py '
|
|
189
|
+
'Provide full path')
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Which statistic to compute (mean or mean+sd)
|
|
193
|
+
parser.add_argument(
|
|
194
|
+
'--mean-only',
|
|
195
|
+
action='store_true',
|
|
196
|
+
help=('Use this flag to compute only input features means '
|
|
197
|
+
'and not the standard deviations')
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
parser.add_argument(
|
|
201
|
+
'--ncpus',
|
|
202
|
+
type=int,
|
|
203
|
+
default=1,
|
|
204
|
+
help=('Number of cpus for parallel computation of means '
|
|
205
|
+
'and of standard deviations. Default: %(default)i')
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Output
|
|
209
|
+
parser.add_argument(
|
|
210
|
+
'--out',
|
|
211
|
+
type=str,
|
|
212
|
+
help=('Optional filename for input features statistics file. '
|
|
213
|
+
'If not provided the file will be named '
|
|
214
|
+
'input_features_stats_datasetFilename_date')
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return parser.parse_args()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == '__main__':
|
|
221
|
+
main()
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import multiprocessing as mp
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
import h5py
|
|
10
|
+
|
|
11
|
+
import helpers.dataset_utils as du
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Additive encoding : genotypes 0, 1 and 2
|
|
15
|
+
NB_POSSIBLE_GENOTYPES = 3
|
|
16
|
+
|
|
17
|
+
# Class label key in dataset file
|
|
18
|
+
CLASS_LABEL = 'class_labels'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
start_time = time.time()
|
|
23
|
+
args = parse_args()
|
|
24
|
+
|
|
25
|
+
# Hdf5 dataset
|
|
26
|
+
dataset_file = h5py.File(args.dataset, 'r')
|
|
27
|
+
|
|
28
|
+
# Indices by fold
|
|
29
|
+
partition_data = np.load(args.partition, allow_pickle=True)
|
|
30
|
+
indices_byfold = partition_data['folds_indexes']
|
|
31
|
+
|
|
32
|
+
print('\n---')
|
|
33
|
+
print('Computing embedding by fold using {} cpu(s)'.format(args.ncpus))
|
|
34
|
+
if args.ncpus > 1:
|
|
35
|
+
pool = mp.Pool(processes=args.ncpus) #this starts ncpus nb of processes
|
|
36
|
+
|
|
37
|
+
# Results that will be returned by processes (embedding for a given fold)
|
|
38
|
+
results = []
|
|
39
|
+
|
|
40
|
+
for fold in range(len(indices_byfold)):
|
|
41
|
+
# Fold indices
|
|
42
|
+
fold_indices = indices_byfold[fold]
|
|
43
|
+
# Get fold data
|
|
44
|
+
(_,_,_,
|
|
45
|
+
x_train, y_train, _,
|
|
46
|
+
x_valid, y_valid, _,
|
|
47
|
+
x_test, y_test,_) = du.get_fold_data(fold, indices_byfold, dataset_file, label=CLASS_LABEL)
|
|
48
|
+
|
|
49
|
+
# Embedding on train+valid sets
|
|
50
|
+
if args.include_valid:
|
|
51
|
+
print('Including valid samples in emb computation')
|
|
52
|
+
x = np.concatenate((x_train, x_valid))
|
|
53
|
+
y = np.concatenate((y_train, y_valid))
|
|
54
|
+
# Embedding on valid set
|
|
55
|
+
elif args.only_valid:
|
|
56
|
+
x = x_valid
|
|
57
|
+
y = y_valid
|
|
58
|
+
# Embedding on test set
|
|
59
|
+
elif args.only_test:
|
|
60
|
+
x = x_test
|
|
61
|
+
y = y_test
|
|
62
|
+
# Embedding on training set
|
|
63
|
+
else:
|
|
64
|
+
x = x_train
|
|
65
|
+
y = y_train
|
|
66
|
+
|
|
67
|
+
# Compute embedding
|
|
68
|
+
#emb = compute_fold_embedding(x, y)
|
|
69
|
+
|
|
70
|
+
print('Computing embedding using {} samples'.format(len(y)))
|
|
71
|
+
# One cpu
|
|
72
|
+
if args.ncpus == 1:
|
|
73
|
+
results.append(compute_fold_embedding(x, y, fold))
|
|
74
|
+
else:
|
|
75
|
+
results.append(pool.apply_async(
|
|
76
|
+
compute_fold_embedding, args=(x,y,fold)))
|
|
77
|
+
|
|
78
|
+
# Close access to dataset file
|
|
79
|
+
dataset_file.close()
|
|
80
|
+
|
|
81
|
+
# Results values: [(fold0, embedding0), (fold1, embedding1) ...]
|
|
82
|
+
print('Computed embedding of every fold, getting the results')
|
|
83
|
+
if args.ncpus > 1:
|
|
84
|
+
results_values = [p.get() for p in results]
|
|
85
|
+
else:
|
|
86
|
+
results_values = results
|
|
87
|
+
|
|
88
|
+
# Order results by fold
|
|
89
|
+
print('Putting embeddings by fold order')
|
|
90
|
+
folds_order = np.array([i[0] for i in results_values])
|
|
91
|
+
|
|
92
|
+
# Order counts (nb samples) per class
|
|
93
|
+
classes_counts = np.array([i[1] for i in results_values])
|
|
94
|
+
classes_counts_ordered = classes_counts[folds_order]
|
|
95
|
+
|
|
96
|
+
# Order embeddings
|
|
97
|
+
embeddings = np.array([i[2] for i in results_values])
|
|
98
|
+
embeddings_ordered = embeddings[folds_order]
|
|
99
|
+
|
|
100
|
+
print('Computed embedding of {} folds with following '
|
|
101
|
+
'number of samples per class for each fold: {}'.format(
|
|
102
|
+
len(folds_order), classes_counts_ordered))
|
|
103
|
+
print('---')
|
|
104
|
+
|
|
105
|
+
# Write embedding to file
|
|
106
|
+
if args.out is not None:
|
|
107
|
+
emb_filename = args.out
|
|
108
|
+
else:
|
|
109
|
+
emb_filename = 'genotype_class_freq_embedding_' \
|
|
110
|
+
+ args.dataset.split('/')[-1][0:-5] \
|
|
111
|
+
+ '_' \
|
|
112
|
+
+ datetime.now().strftime("%Y_%m_%d_%Hh%Mmin%Ssec")
|
|
113
|
+
|
|
114
|
+
emb_fullpath = os.path.join(args.exp_path, emb_filename)
|
|
115
|
+
|
|
116
|
+
print('\n---\nEmbeddings generated in {} seconds'.format(
|
|
117
|
+
time.time()-start_time))
|
|
118
|
+
|
|
119
|
+
print('Saving results to {}'.format(emb_fullpath))
|
|
120
|
+
np.savez(emb_fullpath, emb=embeddings_ordered)
|
|
121
|
+
|
|
122
|
+
print('---\n')
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def compute_fold_embedding(xs, ys, fold):
|
|
126
|
+
# Total number of classes
|
|
127
|
+
nb_class = ys.max() + 1 #class 0
|
|
128
|
+
class_to_count = {}
|
|
129
|
+
|
|
130
|
+
# Compute sum of genotypes (0-1-2) per class
|
|
131
|
+
xs = xs.transpose() # rows are snps, col are inds
|
|
132
|
+
embedding = np.zeros((xs.shape[0],nb_class*NB_POSSIBLE_GENOTYPES))
|
|
133
|
+
for c in range(nb_class):
|
|
134
|
+
# Select genotypes for samples of same class
|
|
135
|
+
class_genotypes = xs[:,ys==c]
|
|
136
|
+
nb = class_genotypes.shape[1] #nb of samples in that class
|
|
137
|
+
#print('Class:', c, 'NB:', nb)
|
|
138
|
+
class_to_count[c] = nb
|
|
139
|
+
for genotype in range(NB_POSSIBLE_GENOTYPES):
|
|
140
|
+
col = NB_POSSIBLE_GENOTYPES*c+genotype
|
|
141
|
+
embedding[:,col] = (class_genotypes == genotype).sum(axis=1)/nb
|
|
142
|
+
|
|
143
|
+
return fold, class_to_count, embedding
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def parse_args():
|
|
148
|
+
parser = argparse.ArgumentParser(
|
|
149
|
+
description='Generate embedding'
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
'--exp-path',
|
|
154
|
+
type=str,
|
|
155
|
+
required=True,
|
|
156
|
+
help='Path to directory where embedding will be saved. '
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
parser.add_argument(
|
|
160
|
+
'--dataset',
|
|
161
|
+
type=str,
|
|
162
|
+
required=True,
|
|
163
|
+
help=('Hdf5 dataset created with create_dataset.py '
|
|
164
|
+
'Provide full path')
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
'--partition',
|
|
169
|
+
type=str,
|
|
170
|
+
required=True,
|
|
171
|
+
help=('Npz dataset partition returned by partition_data.py '
|
|
172
|
+
'Provide full path')
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
parser.add_argument(
|
|
176
|
+
'--ncpus',
|
|
177
|
+
type=int,
|
|
178
|
+
default=1,
|
|
179
|
+
help=('Number of cpus available to compute folds embedding in '
|
|
180
|
+
'parallel. Default:%(default)i')
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
parser.add_argument(
|
|
184
|
+
'--include-valid',
|
|
185
|
+
action='store_true',
|
|
186
|
+
help=('Use this flag to include samples from the validation set '
|
|
187
|
+
'in the embedding computation.')
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
parser.add_argument(
|
|
191
|
+
'--only-valid',
|
|
192
|
+
action='store_true',
|
|
193
|
+
help=('Use this flag to compute embedding only on samples '
|
|
194
|
+
' in the validation set')
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
parser.add_argument(
|
|
198
|
+
'--only-test',
|
|
199
|
+
action='store_true',
|
|
200
|
+
help=('Use this flag to compute embedding only on samples '
|
|
201
|
+
' in the test set')
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
parser.add_argument(
|
|
205
|
+
'--out',
|
|
206
|
+
type=str,
|
|
207
|
+
help=('Optional filename for embedding file. If not provided '
|
|
208
|
+
'the file will be named '
|
|
209
|
+
'genotype_class_freq_embedding_datasetFilename_date')
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
return parser.parse_args()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == '__main__':
|
|
216
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
import h5py
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
class FoldDataset(torch.utils.data.Dataset):
|
|
10
|
+
# These variables are set in train.py
|
|
11
|
+
dataset_file = None #path to h5py dataset file
|
|
12
|
+
f = None # Dataset file handler (h5py.File in reading mode)
|
|
13
|
+
task_handler = None # Classification or regression
|
|
14
|
+
data_x = None
|
|
15
|
+
data_y = None
|
|
16
|
+
data_samples = None
|
|
17
|
+
|
|
18
|
+
def __init__(self, set_indexes):
|
|
19
|
+
self.set_indexes = set_indexes
|
|
20
|
+
|
|
21
|
+
def __len__(self):
|
|
22
|
+
return len(self.set_indexes)
|
|
23
|
+
|
|
24
|
+
def __getitem__(self, index):
|
|
25
|
+
# dataloader index goes from 0 to length of set
|
|
26
|
+
# data_x, data_y, data_samples groups data from all sets (train + valid + test)
|
|
27
|
+
# to get the item corresponding to dataloader index, we get the index of the sample
|
|
28
|
+
# in the whole dataset
|
|
29
|
+
file_index = self.set_indexes[index]
|
|
30
|
+
|
|
31
|
+
# Input features
|
|
32
|
+
#x = np.array(self.f['inputs'][file_index], dtype=np.int8)
|
|
33
|
+
x = self.data_x[file_index]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Label
|
|
37
|
+
#y = self.task_handler.get_label(self.f, file_index)
|
|
38
|
+
y = self.data_y[file_index]
|
|
39
|
+
"""
|
|
40
|
+
if self.task == 'classification':
|
|
41
|
+
#y = (self.f['class_labels'][file_index]).astype(np.int64)
|
|
42
|
+
y = np.array(self.f['class_labels'][file_index],
|
|
43
|
+
dtype=np.int64)
|
|
44
|
+
elif self.task == 'regression':
|
|
45
|
+
#y = (self.f['regression_labels'][file_index]).astype(np.float32)
|
|
46
|
+
y = np.array(self.f['regression_labels'][file_index],
|
|
47
|
+
dtype=np.float32)
|
|
48
|
+
"""
|
|
49
|
+
# Sample id
|
|
50
|
+
#sample = (self.f['samples'][file_index]).astype(np.str_)
|
|
51
|
+
# SAMPLES ARE INDEX in dataset file (hdf5)
|
|
52
|
+
sample = file_index
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
return x, y, sample
|
|
56
|
+
|
|
57
|
+
def get_samples(self):
|
|
58
|
+
indexes = np.sort(self.set_indexes)
|
|
59
|
+
samples = (self.f['samples'][indexes]).astype(np.str_)
|
|
60
|
+
|
|
61
|
+
return samples
|
|
62
|
+
|
|
63
|
+
class FoldDataset_forBigdataset(torch.utils.data.Dataset):
|
|
64
|
+
# These variables are set in train.py
|
|
65
|
+
dataset_file = None #path to h5py dataset file
|
|
66
|
+
f = None # Dataset file handler (h5py.File in reading mode)
|
|
67
|
+
task_handler = None # Classification or regression
|
|
68
|
+
|
|
69
|
+
def __init__(self, set_indexes):
|
|
70
|
+
self.set_indexes = set_indexes
|
|
71
|
+
|
|
72
|
+
def __len__(self):
|
|
73
|
+
return len(self.set_indexes)
|
|
74
|
+
|
|
75
|
+
def __getitem__(self, index):
|
|
76
|
+
# Data of all sets (train, valid, test) is in one file
|
|
77
|
+
# so we convert the index to match file index
|
|
78
|
+
file_index = self.set_indexes[index]
|
|
79
|
+
|
|
80
|
+
# Input features
|
|
81
|
+
x = np.array(self.f['inputs'][file_index], dtype=np.int8)
|
|
82
|
+
|
|
83
|
+
# Label
|
|
84
|
+
y = self.task_handler.get_label(self.f, file_index)
|
|
85
|
+
"""
|
|
86
|
+
if self.task == 'classification':
|
|
87
|
+
#y = (self.f['class_labels'][file_index]).astype(np.int64)
|
|
88
|
+
y = np.array(self.f['class_labels'][file_index],
|
|
89
|
+
dtype=np.int64)
|
|
90
|
+
elif self.task == 'regression':
|
|
91
|
+
#y = (self.f['regression_labels'][file_index]).astype(np.float32)
|
|
92
|
+
y = np.array(self.f['regression_labels'][file_index],
|
|
93
|
+
dtype=np.float32)
|
|
94
|
+
"""
|
|
95
|
+
# Sample id
|
|
96
|
+
#sample = (self.f['samples'][file_index]).astype(np.str_)
|
|
97
|
+
# SAMPLES ARE INDEX
|
|
98
|
+
sample = np.array(index)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
return x, y, sample
|
|
102
|
+
|
|
103
|
+
def get_samples(self):
|
|
104
|
+
indexes = np.sort(self.set_indexes)
|
|
105
|
+
samples = (self.f['samples'][indexes]).astype(np.str_)
|
|
106
|
+
|
|
107
|
+
return samples
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class IndepTestDataset(torch.utils.data.Dataset):
|
|
111
|
+
|
|
112
|
+
# These variables are set in test_independent_dataset.py
|
|
113
|
+
dataset_file = None # path to h5py dataset file
|
|
114
|
+
f = None # Dataset file handler (h5py.File in reading mode)
|
|
115
|
+
task_handler = None # Classification or regression
|
|
116
|
+
data_x = None
|
|
117
|
+
|
|
118
|
+
def __init__(self, samples):
|
|
119
|
+
self.set_indexes = np.arange(len(samples))
|
|
120
|
+
|
|
121
|
+
def __len__(self):
|
|
122
|
+
return len(self.set_indexes)
|
|
123
|
+
|
|
124
|
+
def __getitem__(self, index):
|
|
125
|
+
# Input features
|
|
126
|
+
#x = np.array(self.f['inputs'][file_index], dtype=np.int8)
|
|
127
|
+
x = self.data_x[index]
|
|
128
|
+
|
|
129
|
+
return index,x
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_fold_data(which_fold, folds_indexes, data, label='labels'):
|
|
134
|
+
# Indices of each set for the fold (0:train, 1:valid, 2:test)
|
|
135
|
+
fold_indexes = folds_indexes[which_fold]
|
|
136
|
+
train_indexes = np.sort(fold_indexes[0]) # sort is a hdf5 requirement
|
|
137
|
+
valid_indexes = np.sort(fold_indexes[1])
|
|
138
|
+
test_indexes = np.sort(fold_indexes[2])
|
|
139
|
+
|
|
140
|
+
# Get data (x,y,samples) of each set (train, valid, test)
|
|
141
|
+
x_train = data['inputs'][train_indexes]
|
|
142
|
+
y_train = data[label][train_indexes]
|
|
143
|
+
samples_train = data['samples'][train_indexes]
|
|
144
|
+
|
|
145
|
+
x_valid = data['inputs'][valid_indexes]
|
|
146
|
+
y_valid = data[label][valid_indexes]
|
|
147
|
+
samples_valid = data['samples'][valid_indexes]
|
|
148
|
+
|
|
149
|
+
x_test = data['inputs'][test_indexes]
|
|
150
|
+
y_test = data[label][test_indexes]
|
|
151
|
+
samples_test = data['samples'][test_indexes]
|
|
152
|
+
|
|
153
|
+
return train_indexes, valid_indexes, test_indexes,\
|
|
154
|
+
x_train, y_train, samples_train,\
|
|
155
|
+
x_valid, y_valid, samples_valid,\
|
|
156
|
+
x_test, y_test, samples_test
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def compute_norm_values(x):
|
|
160
|
+
"""
|
|
161
|
+
x is a tensor
|
|
162
|
+
"""
|
|
163
|
+
# Non missing values
|
|
164
|
+
mask = (x >= 0)
|
|
165
|
+
|
|
166
|
+
# Compute mean of every column (feature)
|
|
167
|
+
with torch.no_grad():
|
|
168
|
+
per_feature_mean = torch.sum(x*mask, dim=0) / torch.sum(mask, dim=0)
|
|
169
|
+
|
|
170
|
+
print('Computed per feature mean')
|
|
171
|
+
|
|
172
|
+
# S.d. of every column (feature)
|
|
173
|
+
per_feature_sd = torch.sqrt(
|
|
174
|
+
torch.sum((x*mask-mask*per_feature_mean)**2, dim=0) / \
|
|
175
|
+
(torch.sum(mask, dim=0) - 1)
|
|
176
|
+
)
|
|
177
|
+
per_feature_sd += 1e-6
|
|
178
|
+
|
|
179
|
+
print('Computed per feature sd')
|
|
180
|
+
|
|
181
|
+
return per_feature_mean, per_feature_sd
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def replace_missing_values(x, per_feature_mean):
|
|
185
|
+
"""
|
|
186
|
+
x and per_feature_mean are tensors
|
|
187
|
+
"""
|
|
188
|
+
mask = (x >= 0)
|
|
189
|
+
|
|
190
|
+
for i in range(x.shape[0]):
|
|
191
|
+
x[i] = mask[i]*x[i] + (~mask[i])*per_feature_mean
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def normalize(x, per_feature_mean, per_feature_sd):
|
|
195
|
+
"""
|
|
196
|
+
x, per_feature_mean and per_feature_sd are tensors
|
|
197
|
+
"""
|
|
198
|
+
x_norm = (x - per_feature_mean) / per_feature_sd
|
|
199
|
+
|
|
200
|
+
return x_norm
|