dietnetwork 0.3.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.
Files changed (71) hide show
  1. dietnetwork-0.3.0/.flake8 +6 -0
  2. dietnetwork-0.3.0/.gitignore +36 -0
  3. dietnetwork-0.3.0/CHANGELOG.md +103 -0
  4. dietnetwork-0.3.0/Dietnet_train_and_evaluate/__init__.py +0 -0
  5. dietnetwork-0.3.0/Dietnet_train_and_evaluate/compute_input_features_statistics.py +221 -0
  6. dietnetwork-0.3.0/Dietnet_train_and_evaluate/generate_embedding.py +216 -0
  7. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/__init__.py +0 -0
  8. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/dataset_utils.py +200 -0
  9. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/log_utils.py +148 -0
  10. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/mainloop_utils.py +395 -0
  11. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/model.py +489 -0
  12. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/model_handlers.py +89 -0
  13. dietnetwork-0.3.0/Dietnet_train_and_evaluate/helpers/task_handlers.py +361 -0
  14. dietnetwork-0.3.0/Dietnet_train_and_evaluate/partition_data.py +331 -0
  15. dietnetwork-0.3.0/Dietnet_train_and_evaluate/train.py +959 -0
  16. dietnetwork-0.3.0/LICENSE +28 -0
  17. dietnetwork-0.3.0/NOTICE +15 -0
  18. dietnetwork-0.3.0/PKG-INFO +249 -0
  19. dietnetwork-0.3.0/README.md +191 -0
  20. dietnetwork-0.3.0/pyproject.toml +149 -0
  21. dietnetwork-0.3.0/reproduction/1kgp_partition_seed124.npz +0 -0
  22. dietnetwork-0.3.0/reproduction/README.md +128 -0
  23. dietnetwork-0.3.0/reproduction/prep.sbatch +43 -0
  24. dietnetwork-0.3.0/reproduction/retrain.sbatch +31 -0
  25. dietnetwork-0.3.0/reproduction/submit_retrain.sh +16 -0
  26. dietnetwork-0.3.0/scripts/__init__.py +0 -0
  27. dietnetwork-0.3.0/scripts/check_sensitive_files.py +294 -0
  28. dietnetwork-0.3.0/scripts/upstream_manifest.py +79 -0
  29. dietnetwork-0.3.0/src/dietnet/__init__.py +11 -0
  30. dietnetwork-0.3.0/src/dietnet/_launch.py +40 -0
  31. dietnetwork-0.3.0/src/dietnet/align.py +188 -0
  32. dietnetwork-0.3.0/src/dietnet/alphagenome.py +208 -0
  33. dietnetwork-0.3.0/src/dietnet/cli.py +479 -0
  34. dietnetwork-0.3.0/src/dietnet/dataset.py +181 -0
  35. dietnetwork-0.3.0/src/dietnet/features.py +267 -0
  36. dietnetwork-0.3.0/src/dietnet/package.py +187 -0
  37. dietnetwork-0.3.0/src/dietnet/plink.py +163 -0
  38. dietnetwork-0.3.0/src/dietnet/predict.py +123 -0
  39. dietnetwork-0.3.0/src/dietnet/published.py +87 -0
  40. dietnetwork-0.3.0/src/dietnet/py.typed +0 -0
  41. dietnetwork-0.3.0/src/dietnet/training.py +176 -0
  42. dietnetwork-0.3.0/src/dietnet/upstream.py +161 -0
  43. dietnetwork-0.3.0/src/dietnet/upstream_manifest.json +53 -0
  44. dietnetwork-0.3.0/tests/README.md +42 -0
  45. dietnetwork-0.3.0/tests/conftest.py +56 -0
  46. dietnetwork-0.3.0/tests/fixtures/build_fixtures.py +210 -0
  47. dietnetwork-0.3.0/tests/fixtures/kgp_mini.bed +0 -0
  48. dietnetwork-0.3.0/tests/fixtures/kgp_mini.bim +2000 -0
  49. dietnetwork-0.3.0/tests/fixtures/kgp_mini.fam +480 -0
  50. dietnetwork-0.3.0/tests/fixtures/kgp_mini_labels.tsv +481 -0
  51. dietnetwork-0.3.0/tests/fixtures/kgp_mini_upstream.hdf5 +0 -0
  52. dietnetwork-0.3.0/tests/fixtures/synthetic.bed +0 -0
  53. dietnetwork-0.3.0/tests/fixtures/synthetic.bim +500 -0
  54. dietnetwork-0.3.0/tests/fixtures/synthetic.fam +200 -0
  55. dietnetwork-0.3.0/tests/fixtures/synthetic_labels.tsv +201 -0
  56. dietnetwork-0.3.0/tests/fixtures/synthetic_trait.tsv +201 -0
  57. dietnetwork-0.3.0/tests/fixtures/synthetic_upstream.hdf5 +0 -0
  58. dietnetwork-0.3.0/tests/integration/test_cli_pipeline.py +263 -0
  59. dietnetwork-0.3.0/tests/integration/test_sdist_contents.py +153 -0
  60. dietnetwork-0.3.0/tests/parity/test_dataset.py +72 -0
  61. dietnetwork-0.3.0/tests/parity/test_predict.py +125 -0
  62. dietnetwork-0.3.0/tests/parity/test_published.py +58 -0
  63. dietnetwork-0.3.0/tests/support.py +113 -0
  64. dietnetwork-0.3.0/tests/unit/test_align.py +179 -0
  65. dietnetwork-0.3.0/tests/unit/test_alphagenome.py +203 -0
  66. dietnetwork-0.3.0/tests/unit/test_docs_match_cli.py +62 -0
  67. dietnetwork-0.3.0/tests/unit/test_egress_guard.py +121 -0
  68. dietnetwork-0.3.0/tests/unit/test_features.py +134 -0
  69. dietnetwork-0.3.0/tests/unit/test_package_metadata.py +143 -0
  70. dietnetwork-0.3.0/tests/unit/test_sensitive_file_check.py +279 -0
  71. dietnetwork-0.3.0/tests/unit/test_upstream_unchanged.py +45 -0
@@ -0,0 +1,6 @@
1
+ [flake8]
2
+ # Matches black's line length, so a line black accepts is never a lint error.
3
+ max-line-length = 100
4
+ extend-ignore = E203,W503,E501,F841,F541
5
+ # HussinLab's code is kept identical to upstream and never linted here.
6
+ extend-exclude = Dietnet_train_and_evaluate,.venv
@@ -0,0 +1,36 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+
5
+ # Private exp files
6
+ .ipynb_checkpoints
7
+ #*.ipynb
8
+ *.csv
9
+ run.sh
10
+ make_attributions_run.sh
11
+ Dietnet/Interpretability/experiment_scripts/*.sh
12
+
13
+ # Debugging and testing
14
+ DATA/
15
+ EXPERIMENT_01/
16
+ Test_dev/
17
+ Dietnet/tests/filenames.py
18
+
19
+ # Model weights
20
+ Models_1KGP_24populations/
21
+
22
+ # Local environments and tool state
23
+ .venv/
24
+ .claude/*
25
+ # The project's Claude Code settings and the data-egress hook they run are
26
+ # shared; everything else under .claude/ (local settings, worktrees) is not.
27
+ !.claude/settings.json
28
+ !.claude/hooks/
29
+ .coverage
30
+ coverage.xml
31
+ # Written by the CI install step
32
+ requirements-ci.txt
33
+
34
+ # The documentation site (uv run mkdocs build)
35
+ site/
36
+ .cache/
@@ -0,0 +1,103 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
6
+ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.0] - 2026-10-26
9
+
10
+ Rebuilt on HussinLab/DIETNETWORK `main` (`5481556e`). The training code is
11
+ upstream's, unmodified. The `dietnet` command line, model packages and
12
+ inference are re-applied around it, replacing this repository's 2022 copy of
13
+ the core, which trained differently from the published model (its main
14
+ dropout was fixed at 0.5; the published config uses 0.1).
15
+
16
+ ### Added
17
+
18
+ - `dietnet build-embedding`: the auxiliary network's SNP features from several
19
+ sources: the published genotype frequencies, any precomputed per-SNP table
20
+ (oriented to the counted allele), and random or shuffled controls, with
21
+ per-block centring and PCA. Packages record which blocks a model used.
22
+ - `dietnet match`: prepare a cohort on CPU; `predict` with the same `--work-dir`
23
+ goes straight to inference (for a GPU job).
24
+ - `generate-embedding` and `train` refuse classes that have no training
25
+ samples in some fold: upstream's embedding is NaN for them (or silently
26
+ drops their columns).
27
+ - `reproduction/`: the published split (seed 124, verified against Zenodo), the
28
+ published models' held-out accuracy (0.9214) pinned by a slow test, and the
29
+ SLURM jobs that retrained all 15 (0.9282).
30
+ - `dietnet train --model mlp`: upstream's MLP baseline (no auxiliary network).
31
+ - `dietnet alphagenome`: AlphaGenome variant effects for every SNP through the
32
+ hosted API, resumable, as a feature table for `build-embedding`
33
+ (`pip install 'dietnetwork[alphagenome]'`).
34
+ - `dietnet create-dataset`: PLINK straight to upstream's HDF5 schema, identical
35
+ to upstream's `create_dataset.py` output and without the text round-trip.
36
+ - `dietnet published`: the 15 published models (Zenodo
37
+ doi:10.5281/zenodo.16943452) as model packages.
38
+ - Allele-aware SNP matching. Dosages that count the other allele are flipped,
39
+ the other strand is recognised, and A/T and C/G SNPs are kept only on an
40
+ exact match. The published D/(D−|C|) missing-SNP scale is applied.
41
+ - Biobank-scale prediction: the cohort is matched once into a contiguous HDF5
42
+ under `--work-dir`, memory-mapped, and shared by every model.
43
+ - `dietnet info`, which reports the upstream commit and whether its files are
44
+ unmodified.
45
+ - Parity tests against upstream: dataset builder, `predict` vs
46
+ `test_independent_dataset.py`, and the published models vs
47
+ `Dietnet_inference/inference.py`.
48
+ - Tooling as in manifold_genetics: uv lock, black/isort/flake8, pre-commit with
49
+ a sensitive-files guard and gitleaks, CI on Python 3.10–3.13 and macOS.
50
+ - Released as `dietnetwork` on PyPI: `release.yml` publishes on a version tag
51
+ through trusted publishing, after checking the tag, a dated CHANGELOG entry,
52
+ the artefacts' contents and a clean-room install (`dietnet info` must find
53
+ the bundled upstream code byte for byte the pinned commit).
54
+ - Tests of what would be published: package metadata
55
+ (`tests/unit/test_package_metadata.py`) and the sdist and wheel themselves
56
+ (`tests/integration/test_sdist_contents.py`: no untracked file, nothing the
57
+ sensitive-files check would flag, upstream code identical), run in a CI
58
+ `package` job.
59
+ - A Claude Code data-egress hook (`.claude/hooks/egress_guard.py`, run by the
60
+ project's `.claude/settings.json`): agents are asked for approval before any
61
+ copy from a remote host, any dump of a genotype or data file over ssh, or any
62
+ upload of one to a host that is not an Alliance cluster, and cannot commit or
63
+ push with `--no-verify`.
64
+ - `LICENSE` (BSD 3-Clause) and `NOTICE`, which keeps HussinLab's upstream code
65
+ outside that licence; `py.typed`.
66
+ - A documentation site (mkdocs-material, `docs/`, the `docs` extra), built
67
+ strictly on every pull request and deployed to GitHub Pages from `master`.
68
+ Its tutorial runs the whole pipeline on the `kgp_mini` fixture on every
69
+ build, and a test keeps `docs/cli.md` naming exactly the CLI's commands and
70
+ options.
71
+
72
+ ### Fixed
73
+
74
+ - `build-embedding` with a `class_genotype_freq` block failed when
75
+ `--dataset` or `--partition` was a relative path: upstream's script runs from
76
+ its own directory and could not find them.
77
+
78
+ ### Changed
79
+
80
+ - `partition`, `generate-embedding`, `compute-input-stats` and `train` now run
81
+ upstream's scripts. Partitions are label-balanced by default, as in the paper.
82
+ - Model packages (format 2) hold upstream's checkpoint, the fold's embedding and
83
+ statistics, a training header and metadata (task, normalization, seed, fold,
84
+ upstream commit). Weights load strictly.
85
+ - The predictions TSV has a header: `sample`, `prediction`, `probability`,
86
+ `model_agreement` (regression: `prediction`, `sd_across_models`).
87
+ - `check` takes `--packages` (held-out accuracy per model) or `--predictions`
88
+ with `--labels`.
89
+ - Commands use `--plink` and `--out`. The old CLI's `--exp-path`-relative file
90
+ names are gone.
91
+ - `dietnet alphagenome` sends one request per SNP and retries transient API
92
+ errors with backoff. About 2% of requests fail at random with gRPC
93
+ `INTERNAL`, and the client's batch call failed a whole 200-SNP chunk on any
94
+ one of them, so a run never got past its first chunk.
95
+
96
+ ### Removed
97
+
98
+ - The 2022 core (`Dietnet/`): model, training loop and dataset code, replaced by
99
+ upstream's.
100
+ - Upstream files the CLI does not run: deprecated code, experiments, upstream's
101
+ inference scripts and `create_dataset.py` (listed in the manifest).
102
+ - `preprocess-plink`, `analyze-population` and the Dropbox model presets.
103
+ Allele-aware matching covers the first; the others return if needed.
@@ -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()