clipnet 0.2.1__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.
- clipnet/__init__.py +9 -0
- clipnet/_calculate_activator_interaction.py +159 -0
- clipnet/_calculate_dataset_params.py +123 -0
- clipnet/_calculate_ism_shuffle.py +157 -0
- clipnet/_calculate_performance_metrics.py +150 -0
- clipnet/_fit_nn.py +63 -0
- clipnet/_get_activation_maps.py +63 -0
- clipnet/_get_filter_gc_content.py +64 -0
- clipnet/_rnn_v10_2k.py +85 -0
- clipnet/_rnn_v10_exp.py +94 -0
- clipnet/_rnn_v11.py +91 -0
- clipnet/attribute.py +209 -0
- clipnet/cgen.py +162 -0
- clipnet/cli.py +431 -0
- clipnet/clipnet.py +473 -0
- clipnet/custom_loss.py +55 -0
- clipnet/epistasis.py +142 -0
- clipnet/ism_shuffle.py +74 -0
- clipnet/rnn_v10.py +96 -0
- clipnet/shuffle.py +128 -0
- clipnet/utils.py +334 -0
- clipnet/visualize.py +333 -0
- clipnet-0.2.1.dist-info/METADATA +23 -0
- clipnet-0.2.1.dist-info/RECORD +28 -0
- clipnet-0.2.1.dist-info/WHEEL +5 -0
- clipnet-0.2.1.dist-info/entry_points.txt +2 -0
- clipnet-0.2.1.dist-info/licenses/LICENSE +21 -0
- clipnet-0.2.1.dist-info/top_level.txt +1 -0
clipnet/__init__.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
## NOT IMPLEMENTED
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Calculate contribution scores using shap.DeepExplainer.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import gc
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import shap
|
|
12
|
+
import tensorflow as tf
|
|
13
|
+
|
|
14
|
+
import .utils
|
|
15
|
+
|
|
16
|
+
# This will fix an error message for running tf.__version__==2.5
|
|
17
|
+
shap.explainers._deep.deep_tf.op_handlers["AddV2"] = (
|
|
18
|
+
shap.explainers._deep.deep_tf.passthrough
|
|
19
|
+
)
|
|
20
|
+
tf.compat.v1.disable_v2_behavior()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main():
|
|
24
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
25
|
+
parser.add_argument("core_promoter_seq", type=str, help="")
|
|
26
|
+
parser.add_argument("motif", type=str, help="Where to write DeepSHAP scores.")
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--model_dir",
|
|
29
|
+
type=str,
|
|
30
|
+
default="ensemble_models",
|
|
31
|
+
help="Directory to load models from",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--mode",
|
|
35
|
+
type=str,
|
|
36
|
+
default="quantity",
|
|
37
|
+
help="Calculate interaction strength using quantity or DFIM",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument("--gpu", action="store_true", help="Enable GPU.")
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--use_specific_gpu",
|
|
42
|
+
type=int,
|
|
43
|
+
default=0,
|
|
44
|
+
help="Index of GPU to use (starting from 0). Does nothing if --gpu is not set.",
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--n_subset",
|
|
48
|
+
type=int,
|
|
49
|
+
default=100,
|
|
50
|
+
help="Maximum number of sequences to use as background. \
|
|
51
|
+
Default is 100 to ensure reasonably fast compute on large datasets.",
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--seed",
|
|
55
|
+
type=int,
|
|
56
|
+
default=617,
|
|
57
|
+
help="Random seed for selecting background sequences.",
|
|
58
|
+
)
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
np.random.seed(args.seed)
|
|
61
|
+
|
|
62
|
+
assert args.mode in [
|
|
63
|
+
"quantity",
|
|
64
|
+
"dfim",
|
|
65
|
+
], "mode must be either quantity or dfim."
|
|
66
|
+
|
|
67
|
+
# Load sequences ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
68
|
+
|
|
69
|
+
concat_ref = "".join(shuffled_ref)
|
|
70
|
+
embedded_promoter = (
|
|
71
|
+
ushuffle.shuffle(
|
|
72
|
+
concat_ref, int(np.floor((1000 - len(core_promoter_seq)) / 2)), 2
|
|
73
|
+
)
|
|
74
|
+
+ core_promoter_seq
|
|
75
|
+
+ ushuffle.shuffle(
|
|
76
|
+
concat_ref, int(np.ceil((1000 - len(core_promoter_seq)) / 2)), 2
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
seqs_to_explain = np.array([utils.OneHotDNA(seq).onehot for seq in sequences])
|
|
80
|
+
|
|
81
|
+
# Perform dinucleotide shuffle on n_subset random sequences
|
|
82
|
+
if len(sequences) < args.n_subset:
|
|
83
|
+
args.n_subset = len(sequences)
|
|
84
|
+
reference = [
|
|
85
|
+
sequences[i]
|
|
86
|
+
for i in np.random.choice(
|
|
87
|
+
np.array(range(len(sequences))), size=args.n_subset, replace=False
|
|
88
|
+
)
|
|
89
|
+
]
|
|
90
|
+
shuffled_reference = [
|
|
91
|
+
utils.kshuffle(rec.seq, random_seed=args.seed)[0] for rec in reference
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
# One-hot encode shuffled sequences
|
|
95
|
+
onehot_reference = np.array(
|
|
96
|
+
[utils.OneHotDNA(seq).onehot for seq in shuffled_reference]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Load model and create explainer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
100
|
+
|
|
101
|
+
if args.gpu:
|
|
102
|
+
gpus = tf.config.experimental.list_physical_devices("GPU")
|
|
103
|
+
for gpu in gpus:
|
|
104
|
+
tf.config.experimental.set_memory_growth(gpu, True)
|
|
105
|
+
tf.config.set_visible_devices(gpus[args.use_specific_gpu], "GPU")
|
|
106
|
+
else:
|
|
107
|
+
tf.config.set_visible_devices([], "GPU")
|
|
108
|
+
|
|
109
|
+
if args.model_fp is None:
|
|
110
|
+
models = [
|
|
111
|
+
tf.keras.models.load_model(f"{args.model_dir}/fold_{i}.h5", compile=False)
|
|
112
|
+
for i in range(1, 10)
|
|
113
|
+
]
|
|
114
|
+
else:
|
|
115
|
+
models = [tf.keras.models.load_model(args.model_fp, compile=False)]
|
|
116
|
+
if args.mode == "quantity":
|
|
117
|
+
contrib = [model.output[1] for model in models]
|
|
118
|
+
else:
|
|
119
|
+
contrib = [
|
|
120
|
+
tf.reduce_mean(
|
|
121
|
+
tf.stop_gradient(tf.nn.softmax(model.output[0], axis=-1))
|
|
122
|
+
* model.output[0],
|
|
123
|
+
axis=-1,
|
|
124
|
+
keepdims=True,
|
|
125
|
+
)
|
|
126
|
+
for model in models
|
|
127
|
+
]
|
|
128
|
+
explainers = [
|
|
129
|
+
shap.DeepExplainer((model.input, contrib), onehot_reference)
|
|
130
|
+
for (model, contrib) in zip(models, contrib)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
# Calculate scores ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
134
|
+
|
|
135
|
+
raw_explanations = {i: [] for i in range(len(explainers))}
|
|
136
|
+
batch_size = 256
|
|
137
|
+
for i in range(len(explainers)):
|
|
138
|
+
for j in range(0, len(seqs_to_explain), batch_size):
|
|
139
|
+
shap_values = explainers[i].shap_values(seqs_to_explain[j : j + batch_size])
|
|
140
|
+
raw_explanations[i].append(shap_values)
|
|
141
|
+
gc.collect()
|
|
142
|
+
|
|
143
|
+
concat_explanations = []
|
|
144
|
+
for k in raw_explanations.keys():
|
|
145
|
+
concat_explanations.append(
|
|
146
|
+
np.concatenate([exp for exp in raw_explanations[k]], axis=1).sum(axis=0)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
mean_explanations = np.array(concat_explanations).mean(axis=0)
|
|
150
|
+
scaled_explanations = mean_explanations * seqs_to_explain
|
|
151
|
+
|
|
152
|
+
# Save scores ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
153
|
+
|
|
154
|
+
np.savez_compressed(args.score_fp, scaled_explanations.swapaxes(1, 2))
|
|
155
|
+
np.savez_compressed(args.seq_fp, (seqs_to_explain / 2).astype(int).swapaxes(1, 2))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
main()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
## NOT IMPLEMENTED. PART OF THE FITTING SCRIPTS
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Calculates dataset parameters needed by clipnet. Supply a path to the processed data
|
|
5
|
+
and an output directory. This script will write json files to the output directory
|
|
6
|
+
for use in model training.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def write_dataset_params(i, datadir, outdir):
|
|
17
|
+
outdir = f"{outdir}/f{i}/"
|
|
18
|
+
os.makedirs(outdir, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
test_folds = [i]
|
|
21
|
+
val_folds = [i % 9 + 1]
|
|
22
|
+
train_folds = [j for j in range(1, 10) if j not in test_folds + val_folds]
|
|
23
|
+
print(train_folds, val_folds, test_folds)
|
|
24
|
+
|
|
25
|
+
dataset_params = {
|
|
26
|
+
"train_seq": [
|
|
27
|
+
os.path.join(datadir, f"concat_sequence_{fold}.npz") for fold in train_folds
|
|
28
|
+
],
|
|
29
|
+
"train_procap": [
|
|
30
|
+
os.path.join(datadir, f"concat_procap_{fold}.npz") for fold in train_folds
|
|
31
|
+
],
|
|
32
|
+
"val_seq": [
|
|
33
|
+
os.path.join(datadir, f"concat_sequence_{fold}.npz") for fold in val_folds
|
|
34
|
+
],
|
|
35
|
+
"val_procap": [
|
|
36
|
+
os.path.join(datadir, f"concat_procap_{fold}.npz") for fold in val_folds
|
|
37
|
+
],
|
|
38
|
+
"test_seq": [
|
|
39
|
+
os.path.join(datadir, f"concat_sequence_{fold}.npz") for fold in test_folds
|
|
40
|
+
],
|
|
41
|
+
"test_procap": [
|
|
42
|
+
os.path.join(datadir, f"concat_procap_{fold}.npz") for fold in test_folds
|
|
43
|
+
],
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
dataset_params["n_train_folds"] = len(train_folds)
|
|
47
|
+
dataset_params["n_val_folds"] = len(val_folds)
|
|
48
|
+
dataset_params["n_test_folds"] = len(test_folds)
|
|
49
|
+
|
|
50
|
+
# Calculate n_samples_per_chunk
|
|
51
|
+
dataset_params["n_samples_per_train_fold"] = [
|
|
52
|
+
np.load(f)["arr_0"].shape[0] for f in dataset_params["train_procap"]
|
|
53
|
+
]
|
|
54
|
+
dataset_params["n_samples_per_val_fold"] = [
|
|
55
|
+
np.load(f)["arr_0"].shape[0] for f in dataset_params["val_procap"]
|
|
56
|
+
]
|
|
57
|
+
dataset_params["n_samples_per_test_fold"] = [
|
|
58
|
+
np.load(f)["arr_0"].shape[0] for f in dataset_params["test_procap"]
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
dataset_params["window_length"] = np.load(dataset_params["train_seq"][0])["arr_0"][
|
|
62
|
+
0
|
|
63
|
+
].shape[0]
|
|
64
|
+
|
|
65
|
+
dataset_params["pad"] = int(dataset_params["window_length"] / 4)
|
|
66
|
+
dataset_params["output_length"] = int(
|
|
67
|
+
2 * (dataset_params["window_length"] - 2 * dataset_params["pad"])
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
dataset_params["weight"] = 1 / 500
|
|
71
|
+
|
|
72
|
+
output_fp = os.path.join(outdir, "dataset_params.json")
|
|
73
|
+
|
|
74
|
+
with open(output_fp, "w") as handle:
|
|
75
|
+
json.dump(dataset_params, handle, indent=4, sort_keys=True)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main():
|
|
79
|
+
parser = argparse.ArgumentParser()
|
|
80
|
+
parser.add_argument("datadir", type=str, help="directory containing data")
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"outdir",
|
|
83
|
+
type=str,
|
|
84
|
+
help="directory to save dataset params to (where models will be saved)",
|
|
85
|
+
)
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--threads",
|
|
88
|
+
type=int,
|
|
89
|
+
default=9,
|
|
90
|
+
help="number of threads to use. Only used if not --fold is not selected.",
|
|
91
|
+
)
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"--fold",
|
|
94
|
+
type=int,
|
|
95
|
+
default=None,
|
|
96
|
+
help="fold to calculate dataset params for (will only run one).",
|
|
97
|
+
)
|
|
98
|
+
args = parser.parse_args()
|
|
99
|
+
if args.threads <= 0:
|
|
100
|
+
raise ValueError("--threads must be a positive integer")
|
|
101
|
+
if args.fold is not None:
|
|
102
|
+
write_dataset_params(args.fold, args.datadir, args.outdir)
|
|
103
|
+
else:
|
|
104
|
+
if args.threads == 1:
|
|
105
|
+
for i in range(1, 10):
|
|
106
|
+
write_dataset_params(i, args.datadir, args.outdir)
|
|
107
|
+
elif args.threads > 1:
|
|
108
|
+
import itertools
|
|
109
|
+
import multiprocessing as mp
|
|
110
|
+
|
|
111
|
+
with mp.Pool(9) as p:
|
|
112
|
+
p.starmap(
|
|
113
|
+
write_dataset_params,
|
|
114
|
+
zip(
|
|
115
|
+
range(1, 10),
|
|
116
|
+
itertools.repeat(args.datadir),
|
|
117
|
+
itertools.repeat(args.outdir),
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
main()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
## NOT IMPLEMENTED
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Calculate ISM shuffle scores.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pyfastx
|
|
13
|
+
import tqdm
|
|
14
|
+
from scipy.spatial.distance import cdist
|
|
15
|
+
|
|
16
|
+
import utils
|
|
17
|
+
|
|
18
|
+
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "4"
|
|
19
|
+
logging.getLogger("tensorflow").setLevel(logging.FATAL)
|
|
20
|
+
import clipnet
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main():
|
|
24
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
25
|
+
parser.add_argument("fasta_fp", type=str, help="Fasta file path.")
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"score_fp", type=str, help="Where to write ISM shuffle results."
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"--model_dir",
|
|
31
|
+
type=str,
|
|
32
|
+
default="ensemble_models/",
|
|
33
|
+
help="directory to load models from.",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--gpu",
|
|
37
|
+
type=int,
|
|
38
|
+
default=None,
|
|
39
|
+
help="Index of GPU to use (starting from 0). If not invoked, uses CPU.",
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--n_shuffles",
|
|
43
|
+
type=int,
|
|
44
|
+
default=5,
|
|
45
|
+
help="Number of shuffles/mutations to perform for each position.",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--mut_size", type=int, default=10, help="Size of mutations to use."
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--edge_padding",
|
|
52
|
+
type=int,
|
|
53
|
+
default=50,
|
|
54
|
+
help="Number of positions from edge that we'll skip mutating on.",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--corr_pseudocount",
|
|
58
|
+
type=float,
|
|
59
|
+
default=1e-6,
|
|
60
|
+
help="Pseudocount for correlation calculation.",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--log_quantity_pseudocount",
|
|
64
|
+
type=float,
|
|
65
|
+
default=1e-3,
|
|
66
|
+
help="Pseudocount for log quantity calculation.",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--seed",
|
|
70
|
+
type=int,
|
|
71
|
+
default=617,
|
|
72
|
+
help="Random seed for generating mutations.",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--silence",
|
|
76
|
+
action="store_true",
|
|
77
|
+
help="Disables progress bars and other non-essential print statements.",
|
|
78
|
+
)
|
|
79
|
+
args = parser.parse_args()
|
|
80
|
+
np.random.seed(args.seed)
|
|
81
|
+
|
|
82
|
+
# Load models ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
83
|
+
|
|
84
|
+
nn = (
|
|
85
|
+
clipnet.CLIPNET(n_gpus=1, use_specific_gpu=args.gpu)
|
|
86
|
+
if args.gpu is not None
|
|
87
|
+
else clipnet.CLIPNET(n_gpus=0)
|
|
88
|
+
)
|
|
89
|
+
ensemble = nn.construct_ensemble(args.model_dir, silence=args.silence)
|
|
90
|
+
|
|
91
|
+
# Load sequences ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
92
|
+
|
|
93
|
+
sequences = pyfastx.Fasta(args.fasta_fp)
|
|
94
|
+
seqs_twohot = utils.get_twohot_fasta_sequences(args.fasta_fp, silence=args.silence)
|
|
95
|
+
|
|
96
|
+
# Calculate ISM shuffle scores ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
97
|
+
|
|
98
|
+
wt_pred = ensemble.predict(seqs_twohot, batch_size=256, verbose=0)
|
|
99
|
+
corr_scores = []
|
|
100
|
+
log_quantity_scores = []
|
|
101
|
+
for i in tqdm.tqdm(
|
|
102
|
+
range(len(sequences)),
|
|
103
|
+
desc="Calculating ISM shuffle scores",
|
|
104
|
+
disable=args.silence,
|
|
105
|
+
):
|
|
106
|
+
corr_score = []
|
|
107
|
+
log_quantity_score = []
|
|
108
|
+
rec = sequences[i]
|
|
109
|
+
positions = range(args.edge_padding, len(rec.seq) - args.edge_padding)
|
|
110
|
+
for shuffle in range(args.n_shuffles):
|
|
111
|
+
mutated_seqs = []
|
|
112
|
+
for pos in positions:
|
|
113
|
+
mut = utils.kshuffle(rec.seq, random_seed=args.seed)[0][: args.mut_size]
|
|
114
|
+
mutated_seq = (
|
|
115
|
+
rec.seq[0 : pos - int(len(mut) / 2)]
|
|
116
|
+
+ mut
|
|
117
|
+
+ rec.seq[pos + int(len(mut) / 2) :]
|
|
118
|
+
)
|
|
119
|
+
mutated_seqs.append(mutated_seq)
|
|
120
|
+
mut_pred = ensemble.predict(
|
|
121
|
+
np.array([utils.TwoHotDNA(seq).twohot for seq in mutated_seqs]),
|
|
122
|
+
batch_size=256,
|
|
123
|
+
verbose=0,
|
|
124
|
+
)
|
|
125
|
+
mut_corr = (
|
|
126
|
+
1
|
|
127
|
+
- cdist(
|
|
128
|
+
np.array(
|
|
129
|
+
[wt_pred[0][i, :]] * len(positions)
|
|
130
|
+
+ np.random.normal(
|
|
131
|
+
0, args.corr_pseudocount, mut_pred[0].shape[1]
|
|
132
|
+
)
|
|
133
|
+
),
|
|
134
|
+
mut_pred[0]
|
|
135
|
+
+ np.random.normal(0, args.corr_pseudocount, mut_pred[0].shape[1]),
|
|
136
|
+
metric="correlation",
|
|
137
|
+
)[0, :]
|
|
138
|
+
)
|
|
139
|
+
mut_log_quantity = np.log10(
|
|
140
|
+
mut_pred[1] + args.log_quantity_pseudocount
|
|
141
|
+
) - np.log10(wt_pred[1][i] + args.log_quantity_pseudocount)
|
|
142
|
+
corr_score.append(mut_corr)
|
|
143
|
+
log_quantity_score.append(mut_log_quantity)
|
|
144
|
+
corr_scores.append(np.array(corr_score).mean(axis=0))
|
|
145
|
+
log_quantity_scores.append(np.array(log_quantity_score).mean(axis=0))
|
|
146
|
+
|
|
147
|
+
# Save scores ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
148
|
+
|
|
149
|
+
np.savez_compressed(
|
|
150
|
+
args.score_fp,
|
|
151
|
+
corr_ism_shuffle=np.array(corr_scores),
|
|
152
|
+
log_quantity_ism_shuffle=np.array(log_quantity_scores),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
main()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
## NOT IMPLEMENTED
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
This script calculates performance metrics given a set of predictions and ground truth.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
|
|
9
|
+
import h5py
|
|
10
|
+
import numpy as np
|
|
11
|
+
import pandas as pd
|
|
12
|
+
from scipy.spatial.distance import jensenshannon
|
|
13
|
+
from scipy.stats import pearsonr, spearmanr
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"predictions",
|
|
20
|
+
type=str,
|
|
21
|
+
help="An hdf5 file containing predictions (track, quantity).",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"observed",
|
|
25
|
+
type=str,
|
|
26
|
+
help="A csv(.gz), npy, or npz file containing the observed procap tracks.",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"output",
|
|
30
|
+
type=str,
|
|
31
|
+
help="An hdf5 file to write the performance metrics to.",
|
|
32
|
+
)
|
|
33
|
+
args = parser.parse_args()
|
|
34
|
+
|
|
35
|
+
# Load predictions
|
|
36
|
+
with h5py.File(args.predictions, "r") as hf:
|
|
37
|
+
track = hf["track"][:]
|
|
38
|
+
if len(hf["quantity"].shape) == 2:
|
|
39
|
+
quantity = hf["quantity"][:, 0]
|
|
40
|
+
else:
|
|
41
|
+
quantity = hf["quantity"][:]
|
|
42
|
+
|
|
43
|
+
# Load observed data
|
|
44
|
+
if args.observed.endswith(".npz") or args.observed.endswith(".npy"):
|
|
45
|
+
observed = np.load(args.observed)
|
|
46
|
+
if args.observed.endswith(".npz"):
|
|
47
|
+
observed = observed["arr_0"]
|
|
48
|
+
elif args.observed.endswith(".csv.gz") or args.observed.endswith(".csv"):
|
|
49
|
+
observed = pd.read_csv(args.observed, header=None, index_col=0).to_numpy()
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"File with observed PRO-cap data ({args.observed}) must be numpy or csv format."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Validate dimensions
|
|
56
|
+
if track.shape[0] != observed.shape[0]:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"n predictions ({track.shape[0]}) and n observed ({observed.shape[0]}) do not match."
|
|
59
|
+
)
|
|
60
|
+
if track.shape[1] > observed.shape[1]:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"Predicted tracks ({track.shape[1]}) are longer than observed ({observed.shape[1]})."
|
|
63
|
+
)
|
|
64
|
+
if (observed.shape[1] - track.shape[1]) % 4 != 0:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"Padding around predicted tracks ({observed.shape[1] - track.shape[1]}) must be divisible by 4."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Trim off padding for observed tracks
|
|
70
|
+
start = (observed.shape[1] - track.shape[1]) // 4
|
|
71
|
+
end = observed.shape[1] // 2 - start
|
|
72
|
+
observed_clipped = observed[
|
|
73
|
+
:,
|
|
74
|
+
np.r_[start:end, observed.shape[1] // 2 + start : observed.shape[1] // 2 + end],
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# Benchmark directionality
|
|
78
|
+
track_directionality = np.log1p(
|
|
79
|
+
track[:, : track.shape[1] // 2].sum(axis=1)
|
|
80
|
+
) - np.log1p(track[:, track.shape[1] // 2 :].sum(axis=1))
|
|
81
|
+
observed_directionality = np.log1p(
|
|
82
|
+
observed_clipped[:, : observed_clipped.shape[1] // 2].sum(axis=1)
|
|
83
|
+
) - np.log1p(observed_clipped[:, observed_clipped.shape[1] // 2 :].sum(axis=1))
|
|
84
|
+
directionality_pearson = pearsonr(track_directionality, observed_directionality)
|
|
85
|
+
|
|
86
|
+
# Benchmark TSS position
|
|
87
|
+
strand_break = track.shape[1] // 2
|
|
88
|
+
pred_tss = np.concatenate(
|
|
89
|
+
[track[:, :strand_break].argmax(axis=1), track[:, strand_break:].argmax(axis=1)]
|
|
90
|
+
)
|
|
91
|
+
obs_tss = np.concatenate(
|
|
92
|
+
[
|
|
93
|
+
observed_clipped[:, :strand_break].argmax(axis=1),
|
|
94
|
+
observed_clipped[:, strand_break:].argmax(axis=1),
|
|
95
|
+
]
|
|
96
|
+
)
|
|
97
|
+
tss_pos_pearson = pearsonr(pred_tss, obs_tss)
|
|
98
|
+
|
|
99
|
+
# Benchmark profile
|
|
100
|
+
track_pearson = pd.DataFrame(track).corrwith(pd.DataFrame(observed_clipped), axis=1)
|
|
101
|
+
track_js_distance = jensenshannon(track, observed_clipped, axis=1)
|
|
102
|
+
|
|
103
|
+
# Benchmark quantity
|
|
104
|
+
quantity_log_pearson = pearsonr(
|
|
105
|
+
np.log1p(quantity), np.log1p(observed_clipped.sum(axis=1))
|
|
106
|
+
)
|
|
107
|
+
quantity_spearman = spearmanr(quantity, observed_clipped.sum(axis=1))
|
|
108
|
+
|
|
109
|
+
# Print summary
|
|
110
|
+
print(f"Median Track Pearson: {track_pearson.median():.4f}")
|
|
111
|
+
print(
|
|
112
|
+
f"Mean Track Pearson: {track_pearson.mean():.4f} "
|
|
113
|
+
+ f"+/- {track_pearson.std():.4f}"
|
|
114
|
+
)
|
|
115
|
+
print(f"Median Track JS Distance: {pd.Series(track_js_distance).median():.4f} ")
|
|
116
|
+
print(
|
|
117
|
+
f"Mean Track JS Distance: {pd.Series(track_js_distance).mean():.4f} "
|
|
118
|
+
+ f"+/- {pd.Series(track_js_distance).std():.4f}"
|
|
119
|
+
)
|
|
120
|
+
print(f"Track Directionality Pearson: {directionality_pearson[0]:.4f}")
|
|
121
|
+
print(f"TSS Position Pearson: {tss_pos_pearson[0]:.4f}")
|
|
122
|
+
print(f"Quantity Log Pearson: {quantity_log_pearson[0]:.4f}")
|
|
123
|
+
print(f"Quantity Spearman: {quantity_spearman[0]:.4f}")
|
|
124
|
+
|
|
125
|
+
# Save metrics
|
|
126
|
+
with h5py.File(args.output, "w") as hf:
|
|
127
|
+
hf.create_dataset(
|
|
128
|
+
"track_pearson", data=track_pearson.to_numpy(), compression="gzip"
|
|
129
|
+
)
|
|
130
|
+
hf.create_dataset(
|
|
131
|
+
"track_js_distance", data=track_js_distance, compression="gzip"
|
|
132
|
+
)
|
|
133
|
+
hf.create_dataset(
|
|
134
|
+
"track_directionality",
|
|
135
|
+
data=np.array(directionality_pearson),
|
|
136
|
+
compression="gzip",
|
|
137
|
+
)
|
|
138
|
+
hf.create_dataset("tss_pos_pearson", data=tss_pos_pearson, compression="gzip")
|
|
139
|
+
hf.create_dataset(
|
|
140
|
+
"quantity_log_pearson",
|
|
141
|
+
data=np.array(quantity_log_pearson),
|
|
142
|
+
compression="gzip",
|
|
143
|
+
)
|
|
144
|
+
hf.create_dataset(
|
|
145
|
+
"quantity_spearman", data=np.array(quantity_spearman), compression="gzip"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
main()
|
clipnet/_fit_nn.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
## NOT IMPLEMENTED
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
This script fits a NN model using clipnet.py. It requires a NN architecture file, which
|
|
5
|
+
must contain a function named construct_nn that returns a tf.keras.models.Model object.
|
|
6
|
+
It also requires a dataset_params.py file which specifies parameters and file paths
|
|
7
|
+
associated with the dataset of interest.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "4"
|
|
15
|
+
logging.getLogger("tensorflow").setLevel(logging.FATAL)
|
|
16
|
+
from . import clipnet
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
21
|
+
parser.add_argument("model_dir", type=str, help="directory to save models to")
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--prefix",
|
|
24
|
+
type=str,
|
|
25
|
+
default="rnn_v10",
|
|
26
|
+
help="the prefix of the nn architecture file. Must contain a construct_nn \
|
|
27
|
+
method, an opt hash that contains all the optimizer hyperparameters, and a \
|
|
28
|
+
compile_params hash that specifies what loss and metrics are reported. \
|
|
29
|
+
Models will be saved under this prefix.",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--resume_checkpoint",
|
|
33
|
+
type=str,
|
|
34
|
+
default=None,
|
|
35
|
+
help="resume training from this model.",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"--gpu",
|
|
39
|
+
type=int,
|
|
40
|
+
default=None,
|
|
41
|
+
help="Index of GPU to use (starting from 0). If not invoked, uses CPU.",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--n_gpus",
|
|
45
|
+
type=int,
|
|
46
|
+
default=0,
|
|
47
|
+
help="Number of GPUs to use. If not invoked, uses CPU.",
|
|
48
|
+
)
|
|
49
|
+
args = parser.parse_args()
|
|
50
|
+
|
|
51
|
+
if args.n_gpus > 1:
|
|
52
|
+
nn = clipnet.CLIPNET(n_gpus=args.n_gpus, prefix=args.prefix)
|
|
53
|
+
else:
|
|
54
|
+
nn = (
|
|
55
|
+
clipnet.CLIPNET(n_gpus=1, use_specific_gpu=args.gpu, prefix=args.prefix)
|
|
56
|
+
if args.gpu is not None
|
|
57
|
+
else clipnet.CLIPNET(n_gpus=0, prefix=args.prefix)
|
|
58
|
+
)
|
|
59
|
+
nn.fit(model_dir=args.model_dir, resume_checkpoint=args.resume_checkpoint)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
main()
|