Skip to content

Commit d3dd841

Browse files
committed
Fix experiment table in upconversion
1 parent 6a9ecd0 commit d3dd841

File tree

2 files changed

+50
-17
lines changed

2 files changed

+50
-17
lines changed

petab/v2/lint.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,16 @@ class CheckValidPetabIdColumn(ValidationTask):
228228
"""A task to check that a given column contains only valid PEtab IDs."""
229229

230230
def __init__(
231-
self, table_name: str, column_name: str, required_column: bool = True
231+
self,
232+
table_name: str,
233+
column_name: str,
234+
required_column: bool = True,
235+
ignore_nan: bool = False,
232236
):
233237
self.table_name = table_name
234238
self.column_name = column_name
235239
self.required_column = required_column
240+
self.ignore_nan = ignore_nan
236241

237242
def run(self, problem: Problem) -> ValidationIssue | None:
238243
df = getattr(problem, f"{self.table_name}_df")
@@ -248,7 +253,10 @@ def run(self, problem: Problem) -> ValidationIssue | None:
248253
return
249254

250255
try:
251-
check_ids(df[self.column_name].values, kind=self.column_name)
256+
ids = df[self.column_name].values
257+
if self.ignore_nan:
258+
ids = ids[~pd.isna(ids)]
259+
check_ids(ids, kind=self.column_name)
252260
except ValueError as e:
253261
return ValidationError(str(e))
254262

@@ -308,21 +316,26 @@ def run(self, problem: Problem) -> ValidationIssue | None:
308316
except AssertionError as e:
309317
return ValidationError(str(e))
310318

311-
# TODO: introduce some option for validation partial vs full
319+
# TODO: introduce some option for validation of partial vs full
312320
# problem. if this is supposed to be a complete problem, a missing
313321
# condition table should be an error if the measurement table refers
314-
# to conditions
315-
316-
# check that measured experiments
317-
if problem.experiment_df is None:
318-
return
319-
322+
# to conditions, otherwise it should maximally be a warning
320323
used_experiments = set(problem.measurement_df[EXPERIMENT_ID].values)
321-
available_experiments = set(
322-
problem.experiment_df[EXPERIMENT_ID].unique()
324+
# handle default-experiment
325+
used_experiments = set(
326+
filter(
327+
lambda x: not isinstance(x, float) or not np.isnan(x),
328+
used_experiments,
329+
)
330+
)
331+
# check that measured experiments exist
332+
available_experiments = (
333+
set(problem.experiment_df[EXPERIMENT_ID].unique())
334+
if problem.experiment_df is not None
335+
else set()
323336
)
324337
if missing_experiments := (used_experiments - available_experiments):
325-
raise AssertionError(
338+
return ValidationError(
326339
"Measurement table references experiments that "
327340
"are not specified in the experiments table: "
328341
+ str(missing_experiments)
@@ -826,7 +839,7 @@ def append_overrides(overrides):
826839
CheckMeasurementTable(),
827840
CheckConditionTable(),
828841
CheckExperimentTable(),
829-
CheckValidPetabIdColumn("experiment", EXPERIMENT_ID),
842+
CheckValidPetabIdColumn("experiment", EXPERIMENT_ID, ignore_nan=True),
830843
CheckValidPetabIdColumn("experiment", CONDITION_ID),
831844
CheckExperimentConditionsExist(),
832845
CheckObservableTable(),

petab/v2/petab1to2.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ def petab1to2(yaml_config: Path | str, output_dir: Path | str = None):
6363
if get_major_version(yaml_config) != 1:
6464
raise ValueError("PEtab problem is not version 1.")
6565
petab_problem = ProblemV1.from_yaml(yaml_file or yaml_config)
66+
# get rid of conditionName column if present (unsupported in v2)
67+
petab_problem.condition_df = petab_problem.condition_df.drop(
68+
columns=[v1.C.CONDITION_NAME], errors="ignore"
69+
)
6670
if v1.lint_problem(petab_problem):
6771
raise ValueError("Provided PEtab problem does not pass linting.")
6872

@@ -72,8 +76,6 @@ def petab1to2(yaml_config: Path | str, output_dir: Path | str = None):
7276
# Write new YAML file
7377
output_dir = Path(output_dir)
7478
output_dir.mkdir(parents=True, exist_ok=True)
75-
new_yaml_file = output_dir / Path(yaml_file).name
76-
write_yaml(new_yaml_config, new_yaml_file)
7779

7880
# Update tables
7981
# condition tables, observable tables, SBML files, parameter table:
@@ -104,6 +106,19 @@ def petab1to2(yaml_config: Path | str, output_dir: Path | str = None):
104106
def create_experiment_id(sim_cond_id: str, preeq_cond_id: str) -> str:
105107
if not sim_cond_id and not preeq_cond_id:
106108
return ""
109+
# check whether the conditions will exist in the v2 condition table
110+
sim_cond_exists = (
111+
petab_problem.condition_df.loc[sim_cond_id].notna().any()
112+
)
113+
preeq_cond_exists = (
114+
preeq_cond_id
115+
and petab_problem.condition_df.loc[preeq_cond_id].notna().any()
116+
)
117+
if not sim_cond_exists and not preeq_cond_exists:
118+
# if we have only all-NaN conditions, we don't create a new
119+
# experiment
120+
return ""
121+
107122
if preeq_cond_id:
108123
preeq_cond_id = f"{preeq_cond_id}_"
109124
exp_id = f"experiment__{preeq_cond_id}__{sim_cond_id}"
@@ -126,6 +141,8 @@ def create_experiment_id(sim_cond_id: str, preeq_cond_id: str) -> str:
126141
sim_cond_id = row[v1.C.SIMULATION_CONDITION_ID]
127142
preeq_cond_id = row.get(v1.C.PREEQUILIBRATION_CONDITION_ID, "")
128143
exp_id = create_experiment_id(sim_cond_id, preeq_cond_id)
144+
if not exp_id:
145+
continue
129146
if preeq_cond_id:
130147
experiments.append(
131148
{
@@ -167,8 +184,8 @@ def create_experiment_id(sim_cond_id: str, preeq_cond_id: str) -> str:
167184
if v1.C.PREEQUILIBRATION_CONDITION_ID in measurement_df.columns:
168185
measurement_df[
169186
v1.C.PREEQUILIBRATION_CONDITION_ID
170-
] = measurement_df[v1.C.PREEQUILIBRATION_CONDITION_ID].astype(
171-
str
187+
] = measurement_df[v1.C.PREEQUILIBRATION_CONDITION_ID].fillna(
188+
""
172189
)
173190
else:
174191
measurement_df[v1.C.PREEQUILIBRATION_CONDITION_ID] = ""
@@ -209,6 +226,9 @@ def create_experiment_id(sim_cond_id: str, preeq_cond_id: str) -> str:
209226
measurement_df, get_dest_path(measurement_file)
210227
)
211228

229+
new_yaml_file = output_dir / Path(yaml_file).name
230+
write_yaml(new_yaml_config, new_yaml_file)
231+
212232
# validate updated Problem
213233
validation_issues = v2.lint_problem(new_yaml_file)
214234

0 commit comments

Comments
 (0)