PyAntiGen 1.0.9__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.
- framework/AntimonyGen.py +48 -0
- framework/RxnDict_to_antimony.py +594 -0
- framework/TelluriumGen.py +16 -0
- framework/__init__.py +0 -0
- framework/antimony_utils.py +294 -0
- framework/cli.py +229 -0
- framework/data_interpolation.py +340 -0
- framework/isotopomer_tools.py +41 -0
- framework/model_generation.py +46 -0
- framework/models.py +189 -0
- framework/module_base.py +42 -0
- framework/pyantigen.py +51 -0
- framework/rate_laws.py +101 -0
- framework/reaction_creation.py +43 -0
- framework/template/Example/AntiGen_paths.py +23 -0
- framework/template/Example/Engine/Anchor_cache.py +193 -0
- framework/template/Example/Engine/Deadline.py +535 -0
- framework/template/Example/Engine/Evaluator.py +1176 -0
- framework/template/Example/Engine/Event_times.py +491 -0
- framework/template/Example/Engine/Fast_profile.py +701 -0
- framework/template/Example/Engine/Fit_cache.py +329 -0
- framework/template/Example/Engine/Identifiability.py +698 -0
- framework/template/Example/Engine/Model_optimize.py +1483 -0
- framework/template/Example/Engine/Model_simulate.py +124 -0
- framework/template/Example/Engine/Nuisance_sensitivity.py +298 -0
- framework/template/Example/Engine/Optimize.py +6862 -0
- framework/template/Example/Engine/Petab_export.py +398 -0
- framework/template/Example/Engine/Preequil_cache.py +361 -0
- framework/template/Example/Engine/Profile_checkpoint.py +399 -0
- framework/template/Example/Engine/Results.py +395 -0
- framework/template/Example/Engine/Sensitivity_analysis.py +320 -0
- framework/template/Example/Engine/Simulate.py +617 -0
- framework/template/Example/Flipflop_reference.py +401 -0
- framework/template/Example/Model_generate.py +37 -0
- framework/template/Example/Model_run.py +261 -0
- framework/template/Example/Modules/Data.py +63 -0
- framework/template/Example/Modules/Events.py +14 -0
- framework/template/Example/Modules/Experiment.py +194 -0
- framework/template/Example/Modules/Loss_config.py +61 -0
- framework/template/Example/Modules/Observed_species.py +3 -0
- framework/template/Example/Modules/Optimizer_settings.py +258 -0
- framework/template/Example/Modules/Plots.py +89 -0
- framework/template/Example/Modules/Solver_settings.py +16 -0
- framework/template/Example/Modules/Update_opt_parameters.py +24 -0
- framework/template/Example/Modules/Update_parameters.py +49 -0
- framework/template/data/ADneg.csv +27 -0
- framework/template/data/ADpos.csv +27 -0
- framework/template/data/Flipflop.csv +29 -0
- framework/template/data/make_flipflop_data.py +174 -0
- pyantigen-1.0.9.dist-info/METADATA +129 -0
- pyantigen-1.0.9.dist-info/RECORD +55 -0
- pyantigen-1.0.9.dist-info/WHEEL +5 -0
- pyantigen-1.0.9.dist-info/entry_points.txt +2 -0
- pyantigen-1.0.9.dist-info/licenses/LICENSE +21 -0
- pyantigen-1.0.9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import AntiGen_paths
|
|
6
|
+
|
|
7
|
+
REPO_ROOT = AntiGen_paths.REPO_ROOT
|
|
8
|
+
|
|
9
|
+
from framework.AntimonyGen import AntimonyGen
|
|
10
|
+
from framework.TelluriumGen import TelluriumGen
|
|
11
|
+
|
|
12
|
+
from Modules.Experiment import *
|
|
13
|
+
from Modules.Plots import *
|
|
14
|
+
from Engine.Event_times import attach_event_times
|
|
15
|
+
from Engine.Simulate import simulate
|
|
16
|
+
|
|
17
|
+
def run_steady_state(model_text, paths, settings):
|
|
18
|
+
|
|
19
|
+
rss = TelluriumGen(model_text, paths, settings)
|
|
20
|
+
if settings["Verbose"]:
|
|
21
|
+
print("Steady state: ", rss.steadyState())
|
|
22
|
+
print("getFloatingSpeciesIds: ", rss.getFloatingSpeciesIds())
|
|
23
|
+
print("getBoundarySpeciesIds: ", rss.getBoundarySpeciesIds())
|
|
24
|
+
print("getAssignmentRuleIds: ", rss.getAssignmentRuleIds())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if os.path.exists(paths["models_path"]):
|
|
28
|
+
df_ic = pd.read_csv(paths["models_path"])
|
|
29
|
+
if 'Species' in df_ic.columns:
|
|
30
|
+
max_val = 0.0
|
|
31
|
+
vals = {}
|
|
32
|
+
for idx, row in df_ic.iterrows():
|
|
33
|
+
species = row['Species']
|
|
34
|
+
try:
|
|
35
|
+
# using roadrunner's dict-like access which is robust
|
|
36
|
+
val = rss[species]
|
|
37
|
+
vals[idx] = val
|
|
38
|
+
if val > max_val:
|
|
39
|
+
max_val = val
|
|
40
|
+
except RuntimeError:
|
|
41
|
+
pass
|
|
42
|
+
for idx, val in vals.items():
|
|
43
|
+
if val < 1e-10 * max_val:
|
|
44
|
+
val = 0.0
|
|
45
|
+
df_ic.at[idx, 'InitialCondition'] = val
|
|
46
|
+
df_ic.to_csv(paths["models_path"], index=False)
|
|
47
|
+
print(f"Updated InitialConditions in {paths['models_path']}")
|
|
48
|
+
else:
|
|
49
|
+
print(f"No 'Species' column found in {paths['models_path']}")
|
|
50
|
+
return
|
|
51
|
+
else:
|
|
52
|
+
print(f"No InitialConditions file found in {paths['ic_path']}")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
Species = rss.getFloatingSpeciesIds()
|
|
56
|
+
for s in Species:
|
|
57
|
+
print(s, rss.getValue(s))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def run_simulation(model_text, paths, settings, EXPERIMENT_dict, parameter_overrides=None):
|
|
61
|
+
|
|
62
|
+
data_path = paths["data_path"]
|
|
63
|
+
repo_root = paths["repo_root"]
|
|
64
|
+
MODEL_NAME = paths["MODEL_NAME"]
|
|
65
|
+
print("run_simulation", MODEL_NAME)
|
|
66
|
+
save_path = os.path.join(repo_root, "generated", MODEL_NAME, MODEL_NAME + "_events.txt")
|
|
67
|
+
|
|
68
|
+
results_dict = {}
|
|
69
|
+
experiment = EXPERIMENT_dict['EXPERIMENT']
|
|
70
|
+
for label, replicate in experiment.replicates.items():
|
|
71
|
+
print("Label", label)
|
|
72
|
+
df_dict = replicate["Data"](replicate, data_path)
|
|
73
|
+
events = replicate["Events"](replicate,df_dict)
|
|
74
|
+
|
|
75
|
+
with open(save_path, "w") as f:
|
|
76
|
+
f.write(events)
|
|
77
|
+
|
|
78
|
+
full_model_text = model_text + "\n" + events
|
|
79
|
+
|
|
80
|
+
r = TelluriumGen(full_model_text, paths, settings)
|
|
81
|
+
|
|
82
|
+
replicate["Update_parameters"](r, replicate)
|
|
83
|
+
if parameter_overrides:
|
|
84
|
+
for p_name, p_val in parameter_overrides.items():
|
|
85
|
+
try:
|
|
86
|
+
r[p_name] = p_val
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
# After the overrides: a trigger built on an overridden parameter has to
|
|
91
|
+
# resolve against the value this run will actually integrate with.
|
|
92
|
+
attach_event_times(replicate, r, verbose=True)
|
|
93
|
+
|
|
94
|
+
solver_settings = replicate["Solver_settings"](replicate)
|
|
95
|
+
observed_species = replicate["Observed_species"](r)
|
|
96
|
+
results = simulate(r, solver_settings, observed_species)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
results_dict[replicate["Label"]] = {
|
|
100
|
+
"results": results,
|
|
101
|
+
"replicate": replicate,
|
|
102
|
+
"data": df_dict,
|
|
103
|
+
"observed_species": observed_species,
|
|
104
|
+
"solver_settings": solver_settings,
|
|
105
|
+
"events": events
|
|
106
|
+
}
|
|
107
|
+
EXPERIMENT_dict["plot"](paths,results_dict)
|
|
108
|
+
return results_dict
|
|
109
|
+
|
|
110
|
+
def setup_simulation(settings, EXPERIMENT_dict, parameter_overrides=None):
|
|
111
|
+
if settings.get("MODEL_NAME"):
|
|
112
|
+
MODEL_NAME = settings["MODEL_NAME"]
|
|
113
|
+
else:
|
|
114
|
+
MODEL_NAME = AntiGen_paths.MODEL_NAME
|
|
115
|
+
print("setup_simulation", MODEL_NAME)
|
|
116
|
+
|
|
117
|
+
model_text, paths = AntimonyGen(MODEL_NAME, repo_root=REPO_ROOT)
|
|
118
|
+
|
|
119
|
+
if settings["run_steady_state_first"]:
|
|
120
|
+
run_steady_state(model_text, paths, settings)
|
|
121
|
+
|
|
122
|
+
results_dict = run_simulation(model_text, paths, settings, EXPERIMENT_dict, parameter_overrides=parameter_overrides)
|
|
123
|
+
|
|
124
|
+
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Nuisance parameter sensitivity analysis via Latin Hypercube Sampling.
|
|
3
|
+
|
|
4
|
+
Forward-only (no re-optimisation): hold identifiable params at their current
|
|
5
|
+
optimal values, vary the non-identifiable nuisance params across their plausible
|
|
6
|
+
range, and collect the resulting plasma-PK trajectories.
|
|
7
|
+
|
|
8
|
+
The compile-once / reset-reuse pattern mirrors Engine/Optimize.py:run_all().
|
|
9
|
+
r.reset() restores species to initial conditions; parameter values set by
|
|
10
|
+
Update_parameters persist, so only the nuisance overrides need to be applied
|
|
11
|
+
each iteration.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import numpy as np
|
|
16
|
+
import AntiGen_paths
|
|
17
|
+
|
|
18
|
+
REPO_ROOT = AntiGen_paths.REPO_ROOT
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# LHS sampling
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
def _lhs_sample(nuisance_settings, n_samples, seed=42):
|
|
26
|
+
"""
|
|
27
|
+
Return (param_names, samples) where samples has shape (n_samples, n_params).
|
|
28
|
+
FR is sampled uniformly; all other params log-uniformly.
|
|
29
|
+
"""
|
|
30
|
+
from scipy.stats.qmc import LatinHypercube
|
|
31
|
+
|
|
32
|
+
names = list(nuisance_settings.keys())
|
|
33
|
+
sampler = LatinHypercube(d=len(names), seed=seed)
|
|
34
|
+
unit = sampler.random(n=n_samples) # (n_samples, n_params) in [0, 1]
|
|
35
|
+
|
|
36
|
+
samples = np.zeros_like(unit)
|
|
37
|
+
for i, name in enumerate(names):
|
|
38
|
+
lo, hi = nuisance_settings[name]['bounds']
|
|
39
|
+
if name == 'FR':
|
|
40
|
+
samples[:, i] = lo + unit[:, i] * (hi - lo)
|
|
41
|
+
else:
|
|
42
|
+
samples[:, i] = 10 ** (
|
|
43
|
+
np.log10(lo) + unit[:, i] * (np.log10(hi) - np.log10(lo))
|
|
44
|
+
)
|
|
45
|
+
return names, samples
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# PRCC
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
def _compute_prcc(X, y):
|
|
53
|
+
"""
|
|
54
|
+
Partial rank correlation coefficient of each column of X against y.
|
|
55
|
+
Removes linear effects of all other columns before computing correlation.
|
|
56
|
+
"""
|
|
57
|
+
from scipy.stats import rankdata, pearsonr
|
|
58
|
+
|
|
59
|
+
n, p = X.shape
|
|
60
|
+
X_r = np.apply_along_axis(rankdata, 0, X).astype(float)
|
|
61
|
+
y_r = rankdata(y).astype(float)
|
|
62
|
+
|
|
63
|
+
prcc = np.zeros(p)
|
|
64
|
+
for i in range(p):
|
|
65
|
+
other = np.delete(X_r, i, axis=1)
|
|
66
|
+
A = np.column_stack([np.ones(n), other])
|
|
67
|
+
cx = np.linalg.lstsq(A, X_r[:, i], rcond=None)[0]
|
|
68
|
+
cy = np.linalg.lstsq(A, y_r, rcond=None)[0]
|
|
69
|
+
prcc[i], _ = pearsonr(X_r[:, i] - A @ cx, y_r - A @ cy)
|
|
70
|
+
return prcc
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Total plasma antibody helper
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
def _total_plasma(results):
|
|
78
|
+
"""Sum free + all Aβ-bound Antibody_Plasma species."""
|
|
79
|
+
from Modules.utils.centiloid_utils import get_column_index
|
|
80
|
+
|
|
81
|
+
total = None
|
|
82
|
+
for col in ('[Antibody_Plasma]',
|
|
83
|
+
'[AB38__Antibody_Plasma]',
|
|
84
|
+
'[AB40__Antibody_Plasma]',
|
|
85
|
+
'[AB42__Antibody_Plasma]'):
|
|
86
|
+
idx = get_column_index(results, col)
|
|
87
|
+
if idx is not None:
|
|
88
|
+
conc = results[:, idx]
|
|
89
|
+
total = conc if total is None else total + conc
|
|
90
|
+
return total
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Target value helper
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
def _get_target_values(results, observable_type, placebo_ratio=None):
|
|
98
|
+
"""Extract target concentration/values from results based on observable_type."""
|
|
99
|
+
from Modules.utils.centiloid_utils import get_column_index, calculate_centiloids
|
|
100
|
+
|
|
101
|
+
if observable_type == 'plasma_ab':
|
|
102
|
+
return _total_plasma(results)
|
|
103
|
+
elif observable_type == 'centiloid':
|
|
104
|
+
if placebo_ratio is None:
|
|
105
|
+
from Modules.utils.centiloid_utils import calculate_dense_ratio_77
|
|
106
|
+
placebo_ratio = calculate_dense_ratio_77()
|
|
107
|
+
return calculate_centiloids(results, placebo_ratio)
|
|
108
|
+
else:
|
|
109
|
+
# Generic column name
|
|
110
|
+
idx = get_column_index(results, observable_type)
|
|
111
|
+
if idx is not None:
|
|
112
|
+
return results[:, idx]
|
|
113
|
+
# Try with bracket notation
|
|
114
|
+
idx = get_column_index(results, f'[{observable_type}]')
|
|
115
|
+
if idx is not None:
|
|
116
|
+
return results[:, idx]
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ---------------------------------------------------------------------------
|
|
121
|
+
# Main entry point
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
def run_nuisance_sensitivity(settings, nuisance_param_settings, EXPERIMENT_dict,
|
|
125
|
+
n_samples=100):
|
|
126
|
+
"""
|
|
127
|
+
LHS forward-only nuisance parameter sensitivity analysis.
|
|
128
|
+
|
|
129
|
+
For each drug in nuisance_param_settings:
|
|
130
|
+
1. Compile one RoadRunner model per replicate (once).
|
|
131
|
+
2. Run nominal simulation (params already set by Update_parameters).
|
|
132
|
+
3. For each of n_samples LHS combinations: reset + override nuisance
|
|
133
|
+
params + simulate.
|
|
134
|
+
4. Compute PRCC of each nuisance param against mean AUC across replicates.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
output : dict keyed by drug_name
|
|
139
|
+
param_names, samples, prcc, and per-replicate nominal/spaghetti arrays.
|
|
140
|
+
paths : dict
|
|
141
|
+
Paths dict from AntimonyGen (needed by plot functions).
|
|
142
|
+
"""
|
|
143
|
+
from framework.AntimonyGen import AntimonyGen
|
|
144
|
+
from framework.TelluriumGen import TelluriumGen
|
|
145
|
+
from Engine.Simulate import simulate
|
|
146
|
+
|
|
147
|
+
MODEL_NAME = settings.get('MODEL_NAME', AntiGen_paths.MODEL_NAME)
|
|
148
|
+
model_text, paths = AntimonyGen(MODEL_NAME, repo_root=REPO_ROOT)
|
|
149
|
+
data_path = paths['data_path']
|
|
150
|
+
save_dir = os.path.join(paths['repo_root'], 'generated', MODEL_NAME)
|
|
151
|
+
save_path = os.path.join(save_dir, MODEL_NAME + '_events.txt')
|
|
152
|
+
os.makedirs(save_dir, exist_ok=True)
|
|
153
|
+
|
|
154
|
+
experiment = EXPERIMENT_dict['EXPERIMENT']
|
|
155
|
+
output = {}
|
|
156
|
+
|
|
157
|
+
# Check for metadata structure in settings
|
|
158
|
+
if 'params' in nuisance_param_settings:
|
|
159
|
+
drug_settings_dict = nuisance_param_settings['params']
|
|
160
|
+
meta = nuisance_param_settings
|
|
161
|
+
else:
|
|
162
|
+
drug_settings_dict = nuisance_param_settings
|
|
163
|
+
meta = {}
|
|
164
|
+
|
|
165
|
+
# Treat a flat parameter dict as a single nuisance case if no drug names match.
|
|
166
|
+
single_global_nuisance = False
|
|
167
|
+
if isinstance(drug_settings_dict, dict):
|
|
168
|
+
rep_drugs = {rep.get('Drug') for rep in experiment.replicates.values() if rep.get('Drug')}
|
|
169
|
+
is_param_dict = (
|
|
170
|
+
drug_settings_dict
|
|
171
|
+
and all(
|
|
172
|
+
isinstance(v, dict) and 'x0' in v and 'bounds' in v
|
|
173
|
+
for v in drug_settings_dict.values()
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
if is_param_dict and not (rep_drugs & set(drug_settings_dict.keys())):
|
|
177
|
+
drug_settings_dict = {'Nuisance': drug_settings_dict}
|
|
178
|
+
single_global_nuisance = True
|
|
179
|
+
|
|
180
|
+
observable_type = meta.get('target_observable', 'plasma_ab')
|
|
181
|
+
summary_metric = meta.get('summary_metric', 'auc')
|
|
182
|
+
|
|
183
|
+
placebo_ratio = None
|
|
184
|
+
if observable_type == 'centiloid':
|
|
185
|
+
from Modules.utils.centiloid_utils import calculate_dense_ratio_77
|
|
186
|
+
placebo_ratio = calculate_dense_ratio_77()
|
|
187
|
+
|
|
188
|
+
for drug_name, nuisance_settings in drug_settings_dict.items():
|
|
189
|
+
if single_global_nuisance:
|
|
190
|
+
drug_reps = {
|
|
191
|
+
lbl: rep for lbl, rep in experiment.replicates.items()
|
|
192
|
+
if rep.get('Drug') is not None
|
|
193
|
+
}
|
|
194
|
+
else:
|
|
195
|
+
drug_reps = {
|
|
196
|
+
lbl: rep for lbl, rep in experiment.replicates.items()
|
|
197
|
+
if rep.get('Drug') == drug_name
|
|
198
|
+
}
|
|
199
|
+
if not drug_reps:
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
param_names, samples = _lhs_sample(nuisance_settings, n_samples)
|
|
203
|
+
print(f"\n[nuisance] {drug_name}: "
|
|
204
|
+
f"{len(drug_reps)} replicate(s) × {n_samples} LHS samples "
|
|
205
|
+
f"({len(param_names)} nuisance params)")
|
|
206
|
+
|
|
207
|
+
# ── Compile one model per replicate ──────────────────────────────────
|
|
208
|
+
compiled = {}
|
|
209
|
+
for lbl, rep in drug_reps.items():
|
|
210
|
+
df_dict = rep['Data'](rep, data_path)
|
|
211
|
+
events = rep['Events'](rep, df_dict)
|
|
212
|
+
with open(save_path, 'w') as f:
|
|
213
|
+
f.write(events)
|
|
214
|
+
r = TelluriumGen(model_text + '\n' + events, paths)
|
|
215
|
+
rep['Update_parameters'](r, rep)
|
|
216
|
+
compiled[lbl] = {
|
|
217
|
+
'r': r,
|
|
218
|
+
'df_dict': df_dict,
|
|
219
|
+
'solver_settings': rep['Solver_settings'](rep),
|
|
220
|
+
'observed': rep['Observed_species'](r),
|
|
221
|
+
'age': rep['Age'],
|
|
222
|
+
'label': lbl,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
# ── Nominal trajectories (params already set by Update_parameters) ───
|
|
226
|
+
nominal = {}
|
|
227
|
+
for lbl, c in compiled.items():
|
|
228
|
+
c['r'].reset()
|
|
229
|
+
rep = drug_reps[lbl]
|
|
230
|
+
rep['Update_parameters'](c['r'], rep)
|
|
231
|
+
nominal[lbl] = simulate(c['r'], c['solver_settings'], c['observed'], label=lbl)
|
|
232
|
+
|
|
233
|
+
# ── LHS forward simulations ──────────────────────────────────────────
|
|
234
|
+
spaghetti = {lbl: [] for lbl in drug_reps}
|
|
235
|
+
auc_matrix = np.zeros((n_samples, len(drug_reps)))
|
|
236
|
+
|
|
237
|
+
for s_idx, sample in enumerate(samples):
|
|
238
|
+
override = dict(zip(param_names, sample))
|
|
239
|
+
for rep_idx, (lbl, c) in enumerate(compiled.items()):
|
|
240
|
+
r = c['r']
|
|
241
|
+
r.reset()
|
|
242
|
+
rep = drug_reps[lbl]
|
|
243
|
+
rep['Update_parameters'](r, rep)
|
|
244
|
+
param_ids = r.getGlobalParameterIds()
|
|
245
|
+
for pname, pval in override.items():
|
|
246
|
+
if pname in param_ids:
|
|
247
|
+
r[pname] = pval
|
|
248
|
+
try:
|
|
249
|
+
results = simulate(r, c['solver_settings'], c['observed'], label=lbl)
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
print(f" [nuisance] sample {s_idx} {lbl} failed: {exc}")
|
|
252
|
+
spaghetti[lbl].append(None)
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
spaghetti[lbl].append(results)
|
|
256
|
+
|
|
257
|
+
conc = _get_target_values(results, observable_type, placebo_ratio)
|
|
258
|
+
if conc is not None:
|
|
259
|
+
if summary_metric == 'auc':
|
|
260
|
+
time = np.asarray(results['time'])
|
|
261
|
+
t_event = c['age'] * 365 * 24
|
|
262
|
+
mask = time >= t_event
|
|
263
|
+
if mask.sum() > 1:
|
|
264
|
+
auc_matrix[s_idx, rep_idx] = np.trapezoid(
|
|
265
|
+
conc[mask], time[mask]
|
|
266
|
+
)
|
|
267
|
+
elif summary_metric == 'final':
|
|
268
|
+
auc_matrix[s_idx, rep_idx] = conc[-1]
|
|
269
|
+
|
|
270
|
+
# ── PRCC ─────────────────────────────────────────────────────────────
|
|
271
|
+
mean_auc = auc_matrix.mean(axis=1)
|
|
272
|
+
prcc = (
|
|
273
|
+
_compute_prcc(samples, mean_auc)
|
|
274
|
+
if mean_auc.std() > 0
|
|
275
|
+
else np.zeros(len(param_names))
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
print(f" PRCC ({summary_metric.upper()} of {observable_type}):")
|
|
279
|
+
for pn, prc in zip(param_names, prcc):
|
|
280
|
+
print(f" {pn:<35} {prc:+.3f}")
|
|
281
|
+
|
|
282
|
+
output[drug_name] = {
|
|
283
|
+
'param_names': param_names,
|
|
284
|
+
'samples': samples,
|
|
285
|
+
'prcc': prcc,
|
|
286
|
+
'meta': meta,
|
|
287
|
+
'replicates': {
|
|
288
|
+
lbl: {
|
|
289
|
+
'nominal': nominal[lbl],
|
|
290
|
+
'spaghetti': spaghetti[lbl],
|
|
291
|
+
'auc_samples': auc_matrix[:, i],
|
|
292
|
+
'age': compiled[lbl]['age'],
|
|
293
|
+
}
|
|
294
|
+
for i, lbl in enumerate(drug_reps)
|
|
295
|
+
},
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return output, paths
|