Skip to content

Commit 12a07b9

Browse files
dweindldilpath
andauthored
Add support for PEtab problems with multiple condition files (#152)
Allows specifying different conditions in different files. Closes #7 * Apply suggestions from code review Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
1 parent 7a0b77e commit 12a07b9

File tree

2 files changed

+106
-5
lines changed

2 files changed

+106
-5
lines changed

petab/problem.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ def __setstate__(self, state):
8787

8888
@staticmethod
8989
def from_files(
90-
sbml_file: Union[str, Path, None] = None,
91-
condition_file: Union[str, Path, None] = None,
90+
sbml_file: Union[str, Path] = None,
91+
condition_file:
92+
Union[str, Path, Iterable[Union[str, Path]]] = None,
9293
measurement_file: Union[str, Path,
9394
Iterable[Union[str, Path]]] = None,
9495
parameter_file: Union[str, Path,
@@ -115,7 +116,8 @@ def from_files(
115116
observable_df = None
116117

117118
if condition_file:
118-
condition_df = conditions.get_condition_df(condition_file)
119+
condition_df = core.concat_tables(condition_file,
120+
conditions.get_condition_df)
119121

120122
if measurement_file:
121123
# If there are multiple tables, we will merge them
@@ -198,7 +200,10 @@ def from_yaml(yaml_config: Union[Dict, Path, str]) -> 'Problem':
198200

199201
problem0 = yaml_config['problems'][0]
200202

201-
yaml.assert_single_condition_and_sbml_file(problem0)
203+
if len(problem0[SBML_FILES]) > 1:
204+
# TODO https://github.com/PEtab-dev/libpetab-python/issues/6
205+
raise NotImplementedError(
206+
'Support for multiple models is not yet implemented.')
202207

203208
if isinstance(yaml_config[PARAMETER_FILE], list):
204209
parameter_file = [
@@ -213,7 +218,8 @@ def from_yaml(yaml_config: Union[Dict, Path, str]) -> 'Problem':
213218
if problem0[SBML_FILES] else None,
214219
measurement_file=[get_path(f)
215220
for f in problem0[MEASUREMENT_FILES]],
216-
condition_file=get_path(problem0[CONDITION_FILES][0]),
221+
condition_file=[get_path(f)
222+
for f in problem0[CONDITION_FILES]],
217223
parameter_file=parameter_file,
218224
visualization_files=[
219225
get_path(f) for f in problem0.get(VISUALIZATION_FILES, [])],

tests/test_petab.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import copy
22
import pickle
33
import tempfile
4+
from io import StringIO
45
from math import nan
56
from pathlib import Path
7+
from tempfile import TemporaryDirectory
68

79
import libsbml
810
import numpy as np
911
import pandas as pd
1012
import petab
1113
import pytest
1214
from petab.C import *
15+
from yaml import safe_load
1316

1417

1518
@pytest.fixture
@@ -452,6 +455,33 @@ def test_concat_measurements():
452455
petab.measurements.get_measurement_df))
453456

454457

458+
def test_concat_condition_df():
459+
df1 = pd.DataFrame(data={
460+
CONDITION_ID: ['condition1', 'condition2'],
461+
'par1': [1.1, 1.2],
462+
'par2': [2.1, 2.2],
463+
'par3': [3.1, 3.2]
464+
}).set_index(CONDITION_ID)
465+
466+
assert df1.equals(petab.concat_tables(df1, petab.get_condition_df))
467+
468+
df2 = pd.DataFrame(data={
469+
CONDITION_ID: ['condition3'],
470+
'par1': [1.3],
471+
'par2': [2.3],
472+
}).set_index(CONDITION_ID)
473+
474+
df_expected = pd.DataFrame(data={
475+
CONDITION_ID: ['condition1', 'condition2', 'condition3'],
476+
'par1': [1.1, 1.2, 1.3],
477+
'par2': [2.1, 2.2, 2.3],
478+
'par3': [3.1, 3.2, np.nan],
479+
}).set_index(CONDITION_ID)
480+
assert df_expected.equals(
481+
petab.concat_tables((df1, df2), petab.get_condition_df)
482+
)
483+
484+
455485
def test_get_observable_ids(petab_problem): # pylint: disable=W0621
456486
"""Test if observable ids functions returns correct value."""
457487
assert set(petab_problem.get_observable_ids()) == {'observable_1'}
@@ -535,3 +565,68 @@ def test_load_remote():
535565
assert petab_problem.sbml_model is not None
536566
assert petab_problem.measurement_df is not None \
537567
and not petab_problem.measurement_df.empty
568+
569+
570+
def test_problem_from_yaml_v1_empty():
571+
"""Test loading PEtab version 1 yaml without any files"""
572+
yaml_config = """
573+
format_version: 1
574+
parameter_file:
575+
problems:
576+
- condition_files: []
577+
measurement_files: []
578+
observable_files: []
579+
sbml_files: []
580+
"""
581+
yaml_config = safe_load(StringIO(yaml_config))
582+
petab.Problem.from_yaml(yaml_config)
583+
584+
585+
def test_problem_from_yaml_v1_multiple_files():
586+
"""Test loading PEtab version 1 yaml with multiple condition / measurement
587+
/ observable files"""
588+
yaml_config = """
589+
format_version: 1
590+
parameter_file:
591+
problems:
592+
- condition_files: [conditions1.tsv, conditions2.tsv]
593+
measurement_files: [measurements1.tsv, measurements2.tsv]
594+
observable_files: [observables1.tsv, observables2.tsv]
595+
sbml_files: []
596+
"""
597+
598+
with TemporaryDirectory() as tmpdir:
599+
yaml_path = Path(tmpdir, "problem.yaml")
600+
with open(yaml_path, 'w') as f:
601+
f.write(yaml_config)
602+
603+
for i in (1, 2):
604+
condition_df = pd.DataFrame({
605+
CONDITION_ID: [f"condition{i}"],
606+
})
607+
condition_df.set_index([CONDITION_ID], inplace=True)
608+
petab.write_condition_df(condition_df,
609+
Path(tmpdir, f"conditions{i}.tsv"))
610+
611+
measurement_df = pd.DataFrame({
612+
SIMULATION_CONDITION_ID: [f"condition{i}"],
613+
OBSERVABLE_ID: [f"observable{i}"],
614+
TIME: [i],
615+
MEASUREMENT: [1]
616+
})
617+
petab.write_measurement_df(measurement_df,
618+
Path(tmpdir, f"measurements{i}.tsv"))
619+
620+
observables_df = pd.DataFrame({
621+
OBSERVABLE_ID: [f"observable{i}"],
622+
OBSERVABLE_FORMULA: [1],
623+
NOISE_FORMULA: [1],
624+
})
625+
petab.write_observable_df(observables_df,
626+
Path(tmpdir, f"observables{i}.tsv"))
627+
628+
petab_problem = petab.Problem.from_yaml(yaml_path)
629+
630+
assert petab_problem.measurement_df.shape[0] == 2
631+
assert petab_problem.observable_df.shape[0] == 2
632+
assert petab_problem.condition_df.shape[0] == 2

0 commit comments

Comments
 (0)